// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.ComponentModel;
using System.Data.Common;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace System.Data
{
    internal enum SchemaFormat
    {
        Public = 1,
        Remoting = 2,
        WebService = 3,
        RemotingSkipSchema = 4,
        WebServiceSkipSchema = 5
    }

    /// <summary>
    /// </summary>
    internal sealed class XmlTreeGen
    {
        private ArrayList? _constraintNames;
        private Hashtable? _namespaces;
        private Hashtable _autogenerated = default!; // Late-initialized
        private Hashtable? _prefixes;

        private DataSet? _ds;
        private readonly ArrayList _tables = new ArrayList();
        private readonly ArrayList _relations = new ArrayList();

        private XmlDocument? _dc;
        private XmlElement? _sRoot;
        private int _prefixCount;
        private readonly SchemaFormat _schFormat = SchemaFormat.Public;
        private string? _filePath;
        private string? _fileName;
        private string? _fileExt;
        private XmlElement? _dsElement;
        private XmlElement? _constraintSeparator;

        /// <summary>
        /// This converter allows new versions of the framework to write
        /// the assembly version of older versions of the framework.
        /// </summary>
        private Converter<Type, string>? _targetConverter;

        internal XmlTreeGen(SchemaFormat format)
        {
            _schFormat = format;
        }

        internal static void AddExtendedProperties(PropertyCollection? props, XmlElement node)
        {
            AddExtendedProperties(props, node, null);
        }

        internal static void AddExtendedProperties(PropertyCollection? props, XmlElement node, Type? type)
        {
            if (props != null)
            {
                foreach (DictionaryEntry entry in props)
                {
                    string s, v;

                    if (entry.Key is INullable)
                    {
                        s = (string)SqlConvert.ChangeTypeForXML(entry.Key, typeof(string));
                    }
                    else
                    {
                        s = Convert.ToString(entry.Key, CultureInfo.InvariantCulture)!;
                    }

                    if (entry.Value is INullable)
                    {
                        v = (string)SqlConvert.ChangeTypeForXML(entry.Value, typeof(string));
                    }
                    else if (entry.Value is System.Numerics.BigInteger)
                    {
                        v = (string)BigIntegerStorage.ConvertFromBigInteger((System.Numerics.BigInteger)entry.Value, typeof(string), CultureInfo.InvariantCulture);
                    }
                    else
                    {
                        v = Convert.ToString(entry.Value, CultureInfo.InvariantCulture)!;
                    }

                    if (type == typeof(DataRelation))
                    {
                        s = Keywords.MSD_REL_PREFIX + s;
                    }
                    else if (type == typeof(ForeignKeyConstraint))
                    {
                        s = Keywords.MSD_FK_PREFIX + s;
                    }
                    node.SetAttribute(XmlConvert.EncodeLocalName(s), Keywords.MSPROPNS, v);
                }
            }
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        internal void AddXdoProperties(object? instance, XmlElement root)
        {
            if (instance == null)
            {
                return;
            }

            PropertyDescriptorCollection pds = TypeDescriptor.GetProperties(instance);

            if (!((instance is DataSet) || (instance is DataTable) || (instance is DataColumn) || (instance is DataRelation)))
            {
                return;
            }

            for (int i = 0; i < pds.Count; i++)
            {
                AddXdoProperty(pds[i], instance, root);
            }
            return;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        internal void AddXdoProperty(PropertyDescriptor pd, object instance, XmlElement root)
        {
            Type type = pd.PropertyType;
            bool bisDataColumn = false;
            DataColumn? col = null;  // it may cause problem to assign null here, I will need to change this.
            bool bIsSqlType = false;
            bool bImplementsInullable = false;

            if (instance is DataColumn)
            {
                col = (DataColumn)instance;
                bisDataColumn = true;
                bIsSqlType = col.IsSqlType;
                bImplementsInullable = col.ImplementsINullable;
            }

            if (!bImplementsInullable &&
                 type != typeof(string) &&     // DO NOT REMOVE THIS CHECK
                 type != typeof(bool) &&
                 type != typeof(Type) &&
                 type != typeof(object) &&
                 type != typeof(CultureInfo) &&
                 type != typeof(long) &&
                 type != typeof(int))
            {
                return;
            }

            if ((!pd.ShouldSerializeValue(instance) || !ContainsDesignerSerializationVisibleAttribute(pd)) && !bIsSqlType)
            {
                return;
            }

            object? propInst = pd.GetValue(instance);

            if (propInst is InternalDataCollectionBase)
                return;

            if (propInst is PropertyCollection)
            {
                return;
            }
            // SDUB: perf: Why not have this as a table?
            // there are several xdo properties that equal to some xml attributes, we should not explicitly output them.
            if (
                string.Equals(pd.Name, "Namespace", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "PrimaryKey", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "ColumnName", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "DefaultValue", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "TableName", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "DataSetName", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "AllowDBNull", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "Unique", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "NestedInDataSet", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "Locale", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "CaseSensitive", StringComparison.Ordinal) ||
                string.Equals(pd.Name, "RemotingFormat", StringComparison.Ordinal)
            )
            {
                return;
            }

            if (bisDataColumn)
            {
                if (string.Equals(pd.Name, "DataType", StringComparison.Ordinal))
                {
                    string dt = XmlDataTypeName(col!.DataType);
                    if (bIsSqlType || (col.DataType == typeof(System.Numerics.BigInteger)))
                    {
                        root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, col.DataType.FullName);
                    }
                    else if ((dt.Length == 0) || bImplementsInullable || ((dt == Keywords.XSD_ANYTYPE) && (col.XmlDataType != Keywords.XSD_ANYTYPE)) || (col.DataType == typeof(DateTimeOffset)))
                    {
                        // in Whidbey, XmlDataTypeName function changed to return "anyType" for typeof(Object)
                        // should still always hit this code path for all non-built in types
                        // to handle version qualified typeof(Object) and other CDT objects correctly

                        // we must write the output exactly the same way as we read it
                        SetMSDataAttribute(root, col.DataType);
                    }
                    return;
                }
                if (string.Equals(pd.Name, "Attribute", StringComparison.Ordinal))
                {
                    return;
                }
            }

            string? textValue = pd.Converter.ConvertToString(propInst);
            root.SetAttribute(pd.Name, Keywords.MSDNS, textValue);
            return;
        }

        [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields, typeof(DesignerSerializationVisibilityAttribute))]
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "The DynamicDependency ensures the correct members are preserved.")]
        private static bool ContainsDesignerSerializationVisibleAttribute(PropertyDescriptor pd) => pd.Attributes.Contains(DesignerSerializationVisibilityAttribute.Visible);

        internal static string XmlDataTypeName(Type type)
        {
            if (type == typeof(char))
                return "_";         // has to have SimpleType in this column.
            if (type == typeof(byte[]) || type == typeof(SqlBytes))
                return "base64Binary";       // has to have SimpleType in this column.
            if (type == typeof(DateTime) || type == typeof(SqlDateTime))
                return "dateTime";
            if (type == typeof(TimeSpan))
                return "duration";
            if (type == typeof(decimal) || type == typeof(SqlDecimal) || type == typeof(SqlMoney))
                return "decimal";
            if (type == typeof(int))
                return "int";
            if (type == typeof(bool) || type == typeof(SqlBoolean))
                return "boolean";
            if (type == typeof(float) || type == typeof(SqlSingle))
                return "float";
            if (type == typeof(double) || type == typeof(SqlDouble))
                return "double";
            if (type == typeof(sbyte) || type == typeof(SqlByte))
                return "byte";
            if (type == typeof(byte))
                return "unsignedByte";
            if (type == typeof(short) || type == typeof(SqlInt16))
                return "short";
            if (type == typeof(int) || type == typeof(SqlInt32))
                return "int";
            if (type == typeof(long) || type == typeof(SqlInt64))
                return "long";
            if (type == typeof(ushort))
                return "unsignedShort";
            if (type == typeof(uint))
                return "unsignedInt";
            if (type == typeof(ulong))
                return "unsignedLong";
            if (type == typeof(System.Numerics.BigInteger))
                return Keywords.XSD_ANYTYPE; //"integer";
            if (type == typeof(Uri))
                return "anyURI";
            if (type == typeof(SqlBinary))
                return "hexBinary";
            if (type == typeof(string) || type == typeof(SqlGuid) || type == typeof(SqlString) || type == typeof(SqlChars))
                return "string";
            if (type == typeof(object) || type == typeof(SqlXml) || type == typeof(DateTimeOffset))
                return Keywords.XSD_ANYTYPE;

            return string.Empty;
            // by default, if we dont map anything, we will map to String
            // but I can not make Sql Types that will map to string be unmapped, because in schema , I will miss the second part and won't
            // be able to differenciate between string snd SqlString and others that map to String
        }

        private void GenerateConstraintNames(DataTable table, bool fromTable)
        {
            // if constraint created obased on relation and it is self related rel. then add constraint
            StringBuilder? builder = null;
            foreach (Constraint constr in table.Constraints)
            {
                if (fromTable)
                {
                    if (constr is ForeignKeyConstraint)
                    { // if parent table does not exist , no need to create FKConst
                        if (!_tables.Contains(((ForeignKeyConstraint)constr).RelatedTable))
                        {
                            continue;
                        }
                    }
                }

                int nameInt = 0;
                string name = constr.ConstraintName;
                while (_constraintNames!.Contains(name))
                {
                    if (null == builder)
                    {
                        builder = new StringBuilder();
                    }
                    builder.Append(table.TableName).Append('_').Append(constr.ConstraintName);

                    if (0 < nameInt)
                    {
                        builder.Append('_').Append(nameInt);
                    }
                    nameInt++;
                    name = builder.ToString();
                    builder.Length = 0;
                }
                _constraintNames.Add(name);
                constr.SchemaName = name;
            }
        }

        private void GenerateConstraintNames(ArrayList tables)
        {
            for (int i = 0; i < tables.Count; i++)
            {
                GenerateConstraintNames((DataTable)tables[i]!, true);
            }
        }

        private void GenerateConstraintNames(DataSet ds)
        {
            foreach (DataTable dt in ds.Tables)
            {
                GenerateConstraintNames(dt, false);
            }
        }

        //Does the DS or ANY object in it have ExtendedProperties?
        private static bool _PropsNotEmpty(PropertyCollection? props)
        {
            return props != null && props.Count != 0;
        }

        private static bool HaveExtendedProperties(DataSet ds)
        {
            if (_PropsNotEmpty(ds._extendedProperties))
            {
                return true;
            }
            for (int t = 0; t < ds.Tables.Count; t++)
            {
                DataTable table = ds.Tables[t];
                if (_PropsNotEmpty(table._extendedProperties))
                {
                    return true;
                }
                for (int c = 0; c < table.Columns.Count; c++)
                {
                    if (_PropsNotEmpty(table.Columns[c]._extendedProperties))
                    {
                        return true;
                    }
                }
            }
            // What is the best way to enumerate relations? from DataSet of from DataTable?
            for (int r = 0; r < ds.Relations.Count; r++)
            {
                if (_PropsNotEmpty(ds.Relations[r]._extendedProperties))
                {
                    return true;
                }
            }
            // What about constraints?
            return false;
        }// HaveExtendedProperties

        internal void WriteSchemaRoot(XmlElement rootSchema, string targetNamespace)
        {
            /*
                        if (_ds != null)
                            rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName(_ds.DataSetName));
                        else
                            rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName("NewDataSet"));
            */


            if (!string.IsNullOrEmpty(targetNamespace))
            {
                rootSchema.SetAttribute(Keywords.TARGETNAMESPACE, targetNamespace);
                rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, targetNamespace);
            }
            // Add the namespaces
            // rootSchema.SetAttribute(Keywords.XMLNS, Keywords.XSD_ATOM.String));
            rootSchema.SetAttribute(Keywords.XMLNS, targetNamespace);
            rootSchema.SetAttribute(Keywords.XMLNS_XSD, Keywords.XSDNS);
            rootSchema.SetAttribute(Keywords.XMLNS_MSDATA, Keywords.MSDNS);
            if ((_ds != null) && (HaveExtendedProperties(_ds)))
            {
                rootSchema.SetAttribute(Keywords.XMLNS_MSPROP, Keywords.MSPROPNS);
            }

            if (!string.IsNullOrEmpty(targetNamespace))
            {
                rootSchema.SetAttribute(Keywords.XSD_ATTRIBUTEFORMDEFAULT, Keywords.QUALIFIED);
                rootSchema.SetAttribute(Keywords.XSD_ELEMENTFORMDEFAULT, Keywords.QUALIFIED);
            }
        }

        internal static void ValidateColumnMapping(Type columnType)
        {
            if (DataStorage.IsTypeCustomType(columnType))
            {
                throw ExceptionBuilder.InvalidDataColumnMapping(columnType);
            }
        }

        internal void SetupAutoGenerated(DataSet ds)
        {
            foreach (DataTable dt in ds.Tables)
                SetupAutoGenerated(dt);
        }

        internal void SetupAutoGenerated(ArrayList dt)
        {
            for (int i = 0; i < dt.Count; i++)
            {
                SetupAutoGenerated((DataTable)dt[i]!);
            }
        }

        internal void SetupAutoGenerated(DataTable dt)
        {
            foreach (DataColumn col in dt.Columns)
            {
                if (AutoGenerated(col))
                    _autogenerated[col] = col;
            }

            foreach (Constraint cs in dt.Constraints)
            {
                ForeignKeyConstraint? fk = (cs as ForeignKeyConstraint);
                if (null != fk)
                {
                    if (AutoGenerated(fk))
                        _autogenerated[fk] = fk;
                    else
                    {
                        if (_autogenerated[fk.Columns[0]] != null)
                            _autogenerated[fk.Columns[0]] = null;
                        if (_autogenerated[fk.RelatedColumnsReference[0]] != null)
                            _autogenerated[fk.RelatedColumnsReference[0]] = null;
                        // special case of the ghosted constraints:
                        UniqueConstraint? _constraint = (UniqueConstraint?)fk.RelatedTable.Constraints.FindConstraint(new UniqueConstraint("TEMP", fk.RelatedColumnsReference));

                        if (_constraint == null)
                            continue;

                        if (_autogenerated[_constraint] != null)
                            _autogenerated[_constraint] = null;
                        if (_autogenerated[_constraint.Key.ColumnsReference[0]] != null)
                            _autogenerated[_constraint.Key.ColumnsReference[0]] = null;
                    }
                }
                else
                {
                    UniqueConstraint unique = (UniqueConstraint)cs;
                    if (AutoGenerated(unique))
                        _autogenerated[unique] = unique;
                    else
                    {
                        if (_autogenerated[unique.Key.ColumnsReference[0]] != null)
                            _autogenerated[unique.Key.ColumnsReference[0]] = null;
                    }
                }
            }
        }
        private void CreateTablesHierarchy(DataTable dt)
        {
            //            if (!dt.SerializeHierarchy)
            //                return;
            foreach (DataRelation r in dt.ChildRelations)
            {
                if (!_tables.Contains(r.ChildTable))
                {
                    _tables.Add(r.ChildTable);
                    CreateTablesHierarchy(r.ChildTable);
                }
            }
        }

        private void CreateRelations(DataTable dt)
        {
            foreach (DataRelation r in dt.ChildRelations)
            {
                if (!_relations.Contains(r))
                {
                    _relations.Add(r);
                    //                  if (dt.SerializeHierarchy)
                    CreateRelations(r.ChildTable);
                }
            }
        }

        private DataTable[] CreateToplevelTables()
        {
            ArrayList topTables = new ArrayList();
            for (int i = 0; i < _tables.Count; i++)
            {
                DataTable table = (DataTable)_tables[i]!;
                if (table.ParentRelations.Count == 0)
                    topTables.Add(table);
                else
                {
                    bool fNestedButNotSelfNested = false;
                    for (int j = 0; j < table.ParentRelations.Count; j++)
                    {
                        if (table.ParentRelations[j].Nested)
                        {
                            if (table.ParentRelations[j].ParentTable == table)
                            {
                                fNestedButNotSelfNested = false;
                                break;
                            }
                            fNestedButNotSelfNested = true;
                        }
                    }
                    if (!fNestedButNotSelfNested)
                        topTables.Add(table);
                }
            }
            if (topTables.Count == 0)
            {
                return Array.Empty<DataTable>();
            }

            var temp = new DataTable[topTables.Count];
            topTables.CopyTo(temp, 0);
            return temp;
        }

        // SxS: this method can generate XSD files if the input xmlWriter is XmlTextWriter or DataTextWriter and its underlying stream is FileStream
        // These XSDs are located in the same folder as the underlying stream's file path (see SetPath method).
        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void SchemaTree(XmlDocument xd, XmlWriter xmlWriter, DataSet? ds, DataTable? dt, bool writeHierarchy)
        {
            _constraintNames = new ArrayList();
            _autogenerated = new Hashtable();
            bool genSecondary = _filePath != null; //null non-file based streams.

            _dsElement = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);

            DataTable[] top;
            bool fFlat = false;

            DataTable _dt = dt!;

            if (ds != null)
            {
                _ds = ds;
                foreach (DataTable table in ds.Tables)
                {
                    _tables.Add(table);
                }
            }
            else
            {
                if (dt!.DataSet != null)
                {
                    // preserve datatable's dataset to use for xml
                    // if null it would write out document element instead of dt.DataSet.DataSetName
                    _ds = dt.DataSet;
                }
                _tables.Add(dt);
                if (writeHierarchy)
                {
                    CreateTablesHierarchy(dt);
                }
            }

            _dc = xd;

            _namespaces = new Hashtable();
            _prefixes = new Hashtable();

            XmlElement rootSchema = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
            _sRoot = rootSchema;

            // Need to writeid attribute on schema, as webservice relys on it for typeddataset deserialization
            // to get  class name
            if (_ds != null)
            {
                rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName(_ds.DataSetName));
            }
            else
            {
                rootSchema.SetAttribute(Keywords.XSDID, XmlConvert.EncodeLocalName("NewDataSet"));
            }
            if (_ds != null)
            {
                WriteSchemaRoot(rootSchema, _ds.Namespace);
            }
            else
            {
                WriteSchemaRoot(rootSchema, _dt.Namespace);
            }

            // register the root element and associated NS
            if (_schFormat == SchemaFormat.Remoting)
            {
                if (_ds != null)
                {
                    _namespaces[_ds.Namespace] = rootSchema;
                }
                else
                {
                    _namespaces[_dt.Namespace] = rootSchema;
                }
            }

            if (_schFormat != SchemaFormat.Remoting)
            {
                if (_ds != null)
                {
                    _namespaces[_ds.Namespace] = rootSchema;
                    if (_ds.Namespace.Length == 0)
                        _prefixes[_ds.Namespace] = null;
                    else
                    {
                        // generate a prefix for the dataset schema itself.
                        rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, _ds.Namespace);
                        _prefixes[_ds.Namespace] = "mstns";
                    }
                }
            }

            // Generate all the constraint names
            if (ds != null)
                GenerateConstraintNames(ds);
            else
                GenerateConstraintNames(_tables);

            // Setup AutoGenerated table
            if (_schFormat != SchemaFormat.Remoting)
            {
                if (ds != null)
                {
                    SetupAutoGenerated(ds);
                }
                else
                {
                    SetupAutoGenerated(_tables);
                }
            }


            //
            // Output all top level elements, which will recursively invoke to other tables.
            //

            top = ((ds != null) ? ds.TopLevelTables(true) : CreateToplevelTables());

            if (top.Length == 0 || _schFormat == SchemaFormat.WebServiceSkipSchema || _schFormat == SchemaFormat.RemotingSkipSchema)
            {
                // return an empty schema for now.
                // probably we need to throw an exception
                FillDataSetElement(xd, ds, dt);
                rootSchema.AppendChild(_dsElement);
                AddXdoProperties(_ds, _dsElement);
                AddExtendedProperties(ds!._extendedProperties, _dsElement);


                xd.AppendChild(rootSchema);
                xd.Save(xmlWriter);
                xmlWriter.Flush();
                return; // rootSchema content has already been pushed to xmlWriter
            }

            //            if (schFormat != SchemaFormat.WebService && namespaces.Count > 1 && !genSecondary) {
            //               rootSchema.SetAttribute(Keywords.MSD_FRAGMENTCOUNT, Keywords.MSDNS, namespaces.Count.ToString());
            //            }

            // Fill out dataset element
            XmlElement dsCompositor = FillDataSetElement(xd, ds, dt);

            _constraintSeparator = xd.CreateElement(Keywords.XSD_PREFIX, "SHOULDNOTBEHERE", Keywords.XSDNS);
            _dsElement.AppendChild(_constraintSeparator);
            // DataSet properties
            if (_ds != null)
            {
                AddXdoProperties(_ds, _dsElement);
                AddExtendedProperties(_ds._extendedProperties, _dsElement);
            }


            for (int i = 0; i < top.Length; i++)
            {
                XmlElement el = HandleTable(top[i], xd, rootSchema);
                if (((_ds != null) && (_ds.Namespace == top[i].Namespace)) || string.IsNullOrEmpty(top[i].Namespace) || (_schFormat == SchemaFormat.Remoting))
                {
                    bool fNestedInDataset = top[i]._fNestedInDataset;

                    if (((_ds != null) && (_ds.Namespace.Length != 0)) && string.IsNullOrEmpty(top[i].Namespace))
                    {
                        fNestedInDataset = true;
                    }

                    // what if dt has two nested relation , one self nested , the other with dtParent
                    if (top[i].SelfNested)
                    { // regarding above check : is it selfnested!
                        fNestedInDataset = false;
                    }

                    if (top[i].NestedParentsCount > 1)
                    { // if it has multiple parents, it should be global
                        fNestedInDataset = false;
                    }

                    if (fNestedInDataset)
                    { //deal with maxOccurs properly
                        if (top[i].MinOccurs != 1)
                        {
                            el.SetAttribute(Keywords.MINOCCURS, top[i].MinOccurs.ToString(CultureInfo.InvariantCulture));
                        }
                        if (top[i].MaxOccurs == -1)
                        {
                            el.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
                        }
                        else if (top[i].MaxOccurs != 1)
                        {
                            el.SetAttribute(Keywords.MAXOCCURS, top[i].MaxOccurs.ToString(CultureInfo.InvariantCulture));
                        }
                    }

                    if (!fNestedInDataset)
                    {
                        rootSchema.AppendChild(el);
                        XmlElement node = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
                        if ((_ds != null && _ds.Namespace == top[i].Namespace) || string.IsNullOrEmpty(top[i].Namespace) || (_schFormat == SchemaFormat.Remoting))
                            node.SetAttribute(Keywords.REF, top[i].EncodedTableName);
                        else
                            node.SetAttribute(Keywords.REF, ((string)_prefixes[top[i].Namespace]!) + ':' + top[i].EncodedTableName);

                        dsCompositor.AppendChild(node);
                    }
                    else
                        dsCompositor.AppendChild(el);
                }
                else
                {
                    AppendChildWithoutRef(top[i].Namespace, el);
                    XmlElement node = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
                    node.SetAttribute(Keywords.REF, ((string)_prefixes[top[i].Namespace]!) + ':' + top[i].EncodedTableName);
                    dsCompositor.AppendChild(node);
                }
            }


            _dsElement.RemoveChild(_constraintSeparator);
            rootSchema.AppendChild(_dsElement);

            // Output all non-hierarchical relations without constraints

            DataRelation[] rels = Array.Empty<DataRelation>();
            if (ds != null && _tables.Count > 0)
            { // we need to make sure we want to write relation just for tables in list
                rels = new DataRelation[ds.Relations.Count];
                for (int i = 0; i < ds.Relations.Count; i++)
                {
                    rels[i] = ds.Relations[i];
                }
            }
            else if (writeHierarchy && _tables.Count > 0)
            {
                CreateRelations((DataTable)_tables[0]!);
                rels = new DataRelation[_relations.Count];
                _relations.CopyTo(rels, 0);
            }

            XmlElement? nodeAnn = null;
            XmlElement? nodeApp = null;

            for (int i = 0; i < rels.Length; ++i)
            {
                DataRelation rel = rels[i];

                if (!rel.Nested || fFlat)
                {
                    if (rel.ChildKeyConstraint == null)
                    {
                        if (nodeAnn == null)
                        {
                            nodeAnn = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ANNOTATION, Keywords.XSDNS);
                            rootSchema.AppendChild(nodeAnn);

                            nodeApp = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_APPINFO, Keywords.XSDNS);
                            nodeAnn.AppendChild(nodeApp);
                        }
                        Debug.Assert(nodeApp != null, "Need to create <application..> node first.");
                        nodeApp.AppendChild(HandleRelation(rel, xd));
                    }
                }
            }


            XmlComment? comment = null;
            bool isMultipleNamespaceAndStreamingWriter = (_namespaces.Count > 1 && !genSecondary);

            if (_schFormat != SchemaFormat.Remoting && _schFormat != SchemaFormat.RemotingSkipSchema)
            {
                // complete processing of rootSchema
                foreach (string ns in _namespaces.Keys)
                {
                    if (ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace) || string.IsNullOrEmpty(ns))
                    {
                        continue;
                    }
                    XmlElement _import = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_IMPORT, Keywords.XSDNS);
                    _import.SetAttribute(Keywords.XSD_NAMESPACE, ns);
                    if (_schFormat != SchemaFormat.WebService && !isMultipleNamespaceAndStreamingWriter)
                    {
                        _import.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + "_" + _prefixes[ns] + ".xsd");
                    }
                    rootSchema.PrependChild(_import);
                }
                if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
                {
                    rootSchema.SetAttribute(Keywords.MSD_FRAGMENTCOUNT, Keywords.MSDNS, _namespaces.Count.ToString(CultureInfo.InvariantCulture));
                }
                // Post rootSchema content to xmlWriter.
                xd.AppendChild(rootSchema);  // KB
                if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
                {
                    xd.WriteTo(xmlWriter);
                }
                else
                {
                    xd.Save(xmlWriter);
                }

                xd.RemoveChild(rootSchema); //KB

                foreach (string ns in _namespaces.Keys)
                {
                    if (ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace) || string.IsNullOrEmpty(ns))
                    {
                        continue;
                    }

                    XmlWriter? xw = null;

                    if (!genSecondary)
                    {
                        xw = xmlWriter;
                    }
                    else
                    {
                        xw = new XmlTextWriter(_filePath + _fileName + "_" + _prefixes[ns] + ".xsd", null);
                    }

                    try
                    {
                        if (genSecondary)
                        {
                            if (xw is XmlTextWriter)
                            {
                                ((XmlTextWriter)xw).Formatting = Formatting.Indented;
                            }
                            xw.WriteStartDocument(true);
                        }

                        XmlElement tNode = (XmlElement)_namespaces[ns]!;
                        _dc.AppendChild(tNode);

                        foreach (string imp_ns in _namespaces.Keys)
                        {
                            if (ns == imp_ns)
                            {
                                continue; // don't write out yourself
                            }
                            string? prefix = (string?)_prefixes[imp_ns];
                            if (prefix == null)
                            { // only for dataset.Namespace == empty
                                continue; // do nothing
                            }
                            tNode.SetAttribute("xmlns:" + prefix, imp_ns);
                            XmlElement _import2 = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_IMPORT, Keywords.XSDNS);
                            _import2.SetAttribute(Keywords.XSD_NAMESPACE, imp_ns);

                            if (_schFormat != SchemaFormat.WebService && !isMultipleNamespaceAndStreamingWriter)
                            {
                                if (imp_ns == ((_ds != null) ? _ds.Namespace : _dt.Namespace))
                                    _import2.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + _fileExt); // for the dataset namespace don't append anything
                                else
                                    _import2.SetAttribute(Keywords.XSD_SCHEMALOCATION, _fileName + "_" + prefix + ".xsd");
                            }

                            tNode.PrependChild(_import2);
                        }

                        if (_schFormat != SchemaFormat.WebService && isMultipleNamespaceAndStreamingWriter)
                        {
                            _dc.WriteTo(xw);
                        }
                        else
                        {
                            _dc.Save(xw);
                        }
                        _dc.RemoveChild(tNode);
                        if (genSecondary)
                        {
                            xw.WriteEndDocument();
                        }
                    }
                    finally
                    {
                        if (genSecondary)
                        {
                            xw.Close();
                        }
                    }
                }
            }
            else
            {
                xd.AppendChild(rootSchema);
                xd.Save(xmlWriter);
            }
            if (comment != null)
            {
                rootSchema.PrependChild(comment);
            }

            if (!genSecondary)
            {
                xmlWriter.Flush();
            }
            return; // rootSchema;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal XmlElement SchemaTree(XmlDocument xd, DataTable dt)
        {
            _dsElement = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
            _constraintNames = new ArrayList();
            _ds = dt.DataSet;
            _dc = xd;

            _namespaces = new Hashtable();
            _prefixes = new Hashtable();

            if (_schFormat != SchemaFormat.Remoting)
            {
                _autogenerated = new Hashtable();
            }

            XmlElement rootSchema = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
            _sRoot = rootSchema;
            WriteSchemaRoot(rootSchema, dt.Namespace);

            _ = FillDataSetElement(xd, null, dt);

            _constraintSeparator = xd.CreateElement(Keywords.XSD_PREFIX, "SHOULDNOTBEHERE", Keywords.XSDNS);
            _dsElement.AppendChild(_constraintSeparator);


            if (_schFormat != SchemaFormat.Remoting)
            {
                if (_ds != null)
                {
                    _namespaces[_ds.Namespace] = rootSchema;
                    if (_ds.Namespace.Length == 0)
                    {
                        _prefixes[_ds.Namespace] = null;
                    }
                    else
                    {
                        // generate a prefix for the dataset schema itself.
                        rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, _ds.Namespace);
                        _prefixes[_ds.Namespace] = "mstns";
                    }
                }
                else
                {
                    _namespaces[dt.Namespace] = rootSchema;
                    if (dt.Namespace.Length == 0)
                    {
                        _prefixes[dt.Namespace] = null;
                    }
                    else
                    {
                        // generate a prefix for the dataset schema itself.
                        rootSchema.SetAttribute(Keywords.XMLNS_MSTNS, dt.Namespace);
                        _prefixes[dt.Namespace] = "mstns";
                    }
                }
            }

            // Generate all the constraint names
            GenerateConstraintNames(dt, true);

            //
            // Output all top level elements, which will recursively invoke to other tables.
            //

            XmlElement el = HandleTable(dt, xd, rootSchema, false);
            rootSchema.AppendChild(el);

            _dsElement.RemoveChild(_constraintSeparator);
            rootSchema.AppendChild(_dsElement);

            return rootSchema;
        }

        internal XmlElement FillDataSetElement(XmlDocument xd, DataSet? ds, DataTable? dt)
        {
            Debug.Assert(ds == null || dt == null);
            Debug.Assert(_dsElement != null);

            DataSet? dataSet = ds ?? dt!.DataSet;
            if (dataSet != null)
            {
                _dsElement.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(dataSet.DataSetName));
                _dsElement.SetAttribute(Keywords.MSD_ISDATASET, Keywords.MSDNS, Keywords.TRUE);
                if (ds == null)
                    _dsElement.SetAttribute(Keywords.MSD_MAINDATATABLE, Keywords.MSDNS, XmlConvert.EncodeLocalName(((dt!.Namespace.Length == 0) ? dt.TableName : (dt.Namespace + ":" + dt.TableName))));

                // Add CaseSensitive and locale properties
                if (dataSet.CaseSensitive)
                {
                    _dsElement.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, Keywords.TRUE);
                }
                if (dataSet.ShouldSerializeLocale() || !dataSet.Locale.Equals(CultureInfo.CurrentCulture))
                {
                    _dsElement.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, dataSet.Locale.ToString());
                }
                else
                {
                    _dsElement.SetAttribute(Keywords.MSD_USECURRENTLOCALE, Keywords.MSDNS, Keywords.TRUE);
                }
            }
            else
            { // No DataSet
                if (dt != null)
                {
                    _dsElement.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName("NewDataSet"));
                    _dsElement.SetAttribute(Keywords.MSD_ISDATASET, Keywords.MSDNS, Keywords.TRUE);
                    _dsElement.SetAttribute(Keywords.MSD_MAINDATATABLE, Keywords.MSDNS, XmlConvert.EncodeLocalName(((dt.Namespace.Length == 0) ? dt.TableName : (dt.Namespace + ":" + dt.TableName))));

                    if (dt.CaseSensitive)
                    {
                        // it is a bug to go and write casesensitive attrib as 'true', by default
                        _dsElement.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, Keywords.TRUE);
                    }
                    if (dt.ShouldSerializeLocale() || !dt.Locale.Equals(CultureInfo.CurrentCulture))
                    {
                        _dsElement.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, dt.Locale.ToString());
                    }
                    else
                    {
                        _dsElement.SetAttribute(Keywords.MSD_USECURRENTLOCALE, Keywords.MSDNS, Keywords.TRUE);
                    }
                }
            }

            XmlElement type = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS);
            _dsElement.AppendChild(type);
            XmlElement compositor = xd.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_CHOICE, Keywords.XSDNS);
            compositor.SetAttribute(Keywords.MINOCCURS, Keywords.ZERO_DIGIT);
            compositor.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
            type.AppendChild(compositor);

            return compositor;
        }

        internal void SetPath(XmlWriter xw)
        {
            FileStream? fs;

            DataTextWriter? sw = xw as DataTextWriter;
            fs = (sw != null) ? sw.BaseStream as FileStream : null;

            if (fs == null)
            {
                XmlTextWriter? textw = xw as XmlTextWriter;
                if (textw == null)
                    return;
                fs = textw.BaseStream as FileStream;
                if (fs == null)
                    return;
            }

            _filePath = Path.GetDirectoryName(fs.Name);
            _fileName = Path.GetFileNameWithoutExtension(fs.Name);
            _fileExt = Path.GetExtension(fs.Name);
            if (!string.IsNullOrEmpty(_filePath))
                _filePath += "\\";
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(DataSet ds, XmlWriter xw)
        {
            Save(ds, null, xw);
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(DataTable dt, XmlWriter xw)
        {
            XmlDocument doc = new XmlDocument();
            if (_schFormat == SchemaFormat.Public)
            {
                SetPath(xw);
            }
            XmlElement rootSchema = SchemaTree(doc, dt);
            doc.AppendChild(rootSchema);
            doc.Save(xw);
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(DataSet ds, DataTable? dt, XmlWriter xw)
        {
            Save(ds, dt, xw, false);
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(DataSet? ds, DataTable? dt, XmlWriter xw, bool writeHierarchy)
        {
            Save(ds, dt, xw, writeHierarchy, null);
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(DataSet? ds, DataTable? dt, XmlWriter xw, bool writeHierarchy, Converter<Type, string>? multipleTargetConverter)
        {
            _targetConverter = multipleTargetConverter;

            XmlDocument doc = new XmlDocument();
            if (_schFormat == SchemaFormat.Public)
            {
                SetPath(xw);
            }
            if (_schFormat == SchemaFormat.WebServiceSkipSchema && xw.WriteState == WriteState.Element)
            {
                xw.WriteAttributeString(Keywords.MSD, Keywords.MSD_SCHEMASERIALIZATIONMODE, Keywords.MSDNS, Keywords.MSD_EXCLUDESCHEMA);
            }
            SchemaTree(doc, xw, ds, dt, writeHierarchy);
        }

        internal XmlElement HandleRelation(DataRelation rel, XmlDocument dc)
        {
            XmlElement root = dc.CreateElement(Keywords.MSD, Keywords.MSD_RELATION, Keywords.MSDNS);

            // convert relation name to valid xml name
            root.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(rel.RelationName));

            root.SetAttribute(Keywords.MSD_PARENT, Keywords.MSDNS, rel.ParentKey.Table.EncodedTableName);
            root.SetAttribute(Keywords.MSD_CHILD, Keywords.MSDNS, rel.ChildKey.Table.EncodedTableName);

            if ((_ds == null) || (_ds.Tables.InternalIndexOf(rel.ParentKey.Table.TableName) == -3))
                root.SetAttribute(Keywords.MSD_PARENTTABLENS, Keywords.MSDNS, rel.ParentKey.Table.Namespace);

            if ((_ds == null) || (_ds.Tables.InternalIndexOf(rel.ChildKey.Table.TableName) == -3))
                root.SetAttribute(Keywords.MSD_CHILDTABLENS, Keywords.MSDNS, rel.ChildKey.Table.Namespace);

            DataColumn[] key = rel.ParentKey.ColumnsReference;

            string text = key[0].EncodedColumnName;
            StringBuilder? builder = null;
            if (1 < key.Length)
            {
                builder = new StringBuilder();
                builder.Append(text);
                for (int i = 1; i < key.Length; i++)
                {
                    builder.Append(Keywords.MSD_KEYFIELDSEP).Append(key[i].EncodedColumnName);
                }
                text = builder.ToString();
            }
            root.SetAttribute(Keywords.MSD_PARENTKEY, Keywords.MSDNS, text);

            key = rel.ChildKey.ColumnsReference;
            text = key[0].EncodedColumnName;
            if (1 < key.Length)
            {
                if (null != builder)
                {
                    builder.Length = 0;
                }
                else
                {
                    builder = new StringBuilder();
                }
                builder.Append(text);
                for (int i = 1; i < key.Length; i++)
                {
                    builder.Append(Keywords.MSD_KEYFIELDSEP).Append(key[i].EncodedColumnName);
                }
                text = builder.ToString();
            }
            root.SetAttribute(Keywords.MSD_CHILDKEY, Keywords.MSDNS, text);
            AddExtendedProperties(rel._extendedProperties, root);
            return root;
        }

        private static XmlElement? FindSimpleType(XmlElement schema, string name)
        {
            for (XmlNode? n = schema.FirstChild; n != null; n = n.NextSibling)
            {
                if (n is XmlElement e && e.GetAttribute(Keywords.NAME) == name)
                {
                    return e;
                }
            }
            return null;
        }// FindSimpleType

        internal XmlElement GetSchema(string NamespaceURI)
        {
            XmlElement? schemaEl = (XmlElement?)_namespaces![NamespaceURI];
            if (schemaEl == null)
            {
                schemaEl = _dc!.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SCHEMA, Keywords.XSDNS);
                WriteSchemaRoot(schemaEl, NamespaceURI);
                if (!string.IsNullOrEmpty(NamespaceURI))
                {
                    string prefix = Keywords.APP + Convert.ToString(++_prefixCount, CultureInfo.InvariantCulture);
                    _sRoot!.SetAttribute("xmlns:" + prefix, NamespaceURI);
                    schemaEl.SetAttribute("xmlns:" + prefix, NamespaceURI);
                    _prefixes![NamespaceURI] = prefix;
                }
                _namespaces[NamespaceURI] = schemaEl;
            }
            return schemaEl;
        }

        internal void HandleColumnType(DataColumn col, XmlDocument dc, XmlElement root, XmlElement schema)
        {
            Debug.Assert(_prefixes != null);

            string keyword = Keywords.TYPE;
            if (col.ColumnMapping == MappingType.SimpleContent)
                keyword = Keywords.BASE;

            if (col.SimpleType != null)
            {
                // generate simpleType node
                SimpleType? stNode = col.SimpleType;

                while (stNode != null)
                {
                    // for remoting, set the msdata:targetNamespace for the simpleType.
                    XmlNode type;
                    string? name = stNode.Name;

                    if (!string.IsNullOrEmpty(name))
                    {
                        // For remoting, always need to work with root schema's namespace
                        string nSpace = (_schFormat != SchemaFormat.Remoting) ? stNode.Namespace :
                                                   (col.Table!.DataSet != null ? col.Table.DataSet.Namespace : col.Table.Namespace);

                        // for remoting we need to use columns NS, for other cases it is wrong to get Columns NS, we need to take type's namespace
                        XmlElement schNode = GetSchema(nSpace);

                        //SchNode To Ensure BaseSimpleType Prefix is Generated
                        if (stNode.BaseSimpleType != null && stNode.BaseSimpleType.Namespace != null && stNode.BaseSimpleType.Namespace.Length > 0)
                            GetSchema(stNode.BaseSimpleType.Namespace); //it will ensure a prefix has been created for this namespace

                        type = stNode.ToNode(dc, _prefixes, (_schFormat == SchemaFormat.Remoting));

                        if (stNode == col.SimpleType)
                        {
                            string? prefix = (string?)_prefixes[nSpace];
                            // set the columns's type
                            if (prefix != null && prefix.Length > 0)
                            {
                                if (_schFormat != SchemaFormat.Remoting)
                                    root.SetAttribute(keyword, (prefix + ":" + name)); // look at below,this loop assumes we would be here just oen time: Its Wrong
                                else // As all types (in remoting) belong to the same namespace, just write type name
                                    root.SetAttribute(keyword, name);
                            }
                            else
                                root.SetAttribute(keyword, name);
                            // set the root to the actual type, do not overwrite it in the iteration.
                        }

                        XmlElement? elmSimpeType = FindSimpleType(schNode, name);
                        if (elmSimpeType == null)
                        {
                            // if we don't have the defenition for this simpleType yet. Add it
                            schNode.AppendChild(type);
                        }
                        else
                        {
#if DEBUG
                            // enzol: TO DO: replace the constructor with IsEqual(XmlElement)
                            //                        Debug.Assert(col.SimpleType.IsEqual(new SimpleType(elmSimpeType)), $"simpleTypes with the same name have to be the same: {name}");
#endif
                        }
                    }
                    else
                    {
                        //SchNode To Ensure BaseSimpleType Prefix is Generated
                        if (stNode.BaseSimpleType != null && stNode.BaseSimpleType.Namespace != null && stNode.BaseSimpleType.Namespace.Length > 0)
                            GetSchema(stNode.BaseSimpleType.Namespace); //it will ensure a prefix has been created for this namespace
                        type = stNode.ToNode(dc, _prefixes, _schFormat == SchemaFormat.Remoting);
                        root.AppendChild(type);
                    }

                    stNode = stNode.BaseSimpleType;
                }
            }
            else if (col.XmlDataType != null && col.XmlDataType.Length != 0 && XSDSchema.IsXsdType(col.XmlDataType))
            {
                root.SetAttribute(keyword, XSDSchema.QualifiedName(col.XmlDataType));
            }
            else
            {
                string typeName = XmlDataTypeName(col.DataType); // do not update the hashtable, as it will not write msdata:DataType
                if (string.IsNullOrEmpty(typeName))
                {
                    if (col.DataType == typeof(Guid) || col.DataType == typeof(Type))
                    {
                        typeName = "string";
                    }
                    else
                    {
                        if (col.ColumnMapping == MappingType.Attribute)
                        {
                            XmlTreeGen.ValidateColumnMapping(col.DataType);
                        }
                        typeName = Keywords.XSD_ANYTYPE;
                    }
                }
                root.SetAttribute(keyword, XSDSchema.QualifiedName(typeName));
            }
        }

        internal void AddColumnProperties(DataColumn col, XmlElement root)
        {
            if (col.DataType != typeof(string))
            {
                string dt = XmlDataTypeName(col.DataType);
                if ((col.IsSqlType && ((dt.Length == 0) || col.ImplementsINullable)) || (typeof(SqlXml) == col.DataType) || col.DataType == typeof(DateTimeOffset) || col.DataType == typeof(System.Numerics.BigInteger))
                { // no need to check if it is Sql typee if it already implements INullable,
                    root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, col.DataType.FullName);
                }
                else if ((dt.Length == 0) || col.ImplementsINullable || ((dt == Keywords.XSD_ANYTYPE) && (col.XmlDataType != Keywords.XSD_ANYTYPE)))
                {
                    // in Whidbey, XmlDataTypeName function changed to return "anyType" for typeof(Object)
                    // should still always hit this code path for all non-built in types
                    // to handle version qualified typeof(Object) and other CDT objects correctly

                    // we must write the output exactly the same way as we read it
                    SetMSDataAttribute(root, col.DataType);
                }
            }

            if (col.ReadOnly)
                root.SetAttribute("ReadOnly", Keywords.MSDNS, Keywords.TRUE);

            if (col.Expression.Length != 0)
                root.SetAttribute("Expression", Keywords.MSDNS, col.Expression);

            if (col.AutoIncrement)
            {
                root.SetAttribute("AutoIncrement", Keywords.MSDNS, Keywords.TRUE);
            }

            if (col.AutoIncrementSeed != 0)
                root.SetAttribute("AutoIncrementSeed", Keywords.MSDNS, col.AutoIncrementSeed.ToString(CultureInfo.InvariantCulture));

            if (col.AutoIncrementStep != 1)
                root.SetAttribute("AutoIncrementStep", Keywords.MSDNS, col.AutoIncrementStep.ToString(CultureInfo.InvariantCulture));

            if (col.Caption != col.ColumnName)
                root.SetAttribute("Caption", Keywords.MSDNS, col.Caption);

            if (col.Prefix.Length != 0)
                root.SetAttribute("Prefix", Keywords.MSDNS, col.Prefix);

            if (col.DataType == typeof(DateTime) && col.DateTimeMode != DataSetDateTime.UnspecifiedLocal)
            {
                root.SetAttribute("DateTimeMode", Keywords.MSDNS, col.DateTimeMode.ToString());
            }
        }

        private string FindTargetNamespace(DataTable table)
        {
            string tgNamespace = table.TypeName.IsEmpty ? table.Namespace : table.TypeName.Namespace;
            if (string.IsNullOrEmpty(tgNamespace))
            {
                DataRelation[] nestedParentRelations = table.NestedParentRelations;
                if (nestedParentRelations.Length != 0)
                {
                    for (int i = 0; i < nestedParentRelations.Length; i++)
                    {
                        DataTable parentTable = nestedParentRelations[i].ParentTable;
                        if (table != parentTable)
                        {// table can be self nested so it may go to infinite loop!
                            tgNamespace = FindTargetNamespace(parentTable);
                            if (!string.IsNullOrEmpty(tgNamespace))
                            {
                                break;
                            }
                        }
                    }
                }
                else
                { // if it does not have any parent table , then it should inherit NS from DataSet
                    tgNamespace = _ds!.Namespace;
                }
            }
            return tgNamespace;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal XmlElement HandleColumn(DataColumn col, XmlDocument dc, XmlElement schema, bool fWriteOrdinal)
        {
            Debug.Assert(_prefixes != null);
            Debug.Assert(_dc != null);

            XmlElement root;
            int minOccurs;

            Debug.Assert(col.ColumnMapping != MappingType.SimpleContent, "Illegal state");

            string refString = (col.ColumnMapping != MappingType.Element) ? Keywords.XSD_ATTRIBUTE : Keywords.XSD_ELEMENT;
            root = dc.CreateElement(Keywords.XSD_PREFIX, refString, Keywords.XSDNS);

            // First add any attributes.
            root.SetAttribute(Keywords.NAME, col.EncodedColumnName);

            if (col.Namespace.Length == 0)
            {
                DataTable _table = col.Table!;
                // We need to travese the hirerarchy to find the targetnamepace
                string tgNamespace = FindTargetNamespace(_table);
                if (col.Namespace != tgNamespace)
                {
                    root.SetAttribute(Keywords.FORM, Keywords.UNQUALIFIED);
                }
            }

            if (col.GetType() != typeof(DataColumn))
                AddXdoProperties(col, root);
            else
                AddColumnProperties(col, root);


            AddExtendedProperties(col._extendedProperties, root);
            HandleColumnType(col, dc, root, schema);
            if (col.ColumnMapping == MappingType.Hidden)
            { // CDT / UDT can not be mapped to Hidden column
                if (!col.AllowDBNull)
                    root.SetAttribute(Keywords.MSD_ALLOWDBNULL, Keywords.MSDNS, Keywords.FALSE);

                if (!col.DefaultValueIsNull)
                    if (col.DataType == typeof(bool))
                        root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
                    else
                    {
                        XmlTreeGen.ValidateColumnMapping(col.DataType);
                        root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, col.ConvertObjectToXml(col.DefaultValue));
                    }
            }


            if ((!col.DefaultValueIsNull) && (col.ColumnMapping != MappingType.Hidden))
            {
                XmlTreeGen.ValidateColumnMapping(col.DataType);
                if (col.ColumnMapping == MappingType.Attribute && !col.AllowDBNull)
                {
                    if (col.DataType == typeof(bool))
                    {
                        root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
                    }
                    else
                    { // CDT / UDT columns cn not be mapped to Attribute also
                        root.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, col.ConvertObjectToXml(col.DefaultValue));
                    }
                }
                else
                { // Element Column : need to handle CDT
                    if (col.DataType == typeof(bool))
                    {
                        root.SetAttribute(Keywords.DEFAULT, (bool)(col.DefaultValue) ? Keywords.TRUE : Keywords.FALSE);
                    }
                    else
                    {
                        if (!col.IsCustomType)
                        { // built in type
                            root.SetAttribute(Keywords.DEFAULT, col.ConvertObjectToXml(col.DefaultValue));
                        }
                        else
                        { // UDT column
                        }
                    }
                }
            }

            if (_schFormat == SchemaFormat.Remoting)
                root.SetAttribute(Keywords.TARGETNAMESPACE, Keywords.MSDNS, col.Namespace);
            else
            {
                if ((col.Namespace != (col.Table!.TypeName.IsEmpty ? col.Table.Namespace : col.Table.TypeName.Namespace)) && (col.Namespace.Length != 0))
                {
                    XmlElement schNode = GetSchema(col.Namespace);
                    if (FindTypeNode(schNode, col.EncodedColumnName) == null)
                        schNode.AppendChild(root);
                    root = _dc.CreateElement(Keywords.XSD_PREFIX, refString, Keywords.XSDNS);
                    root.SetAttribute(Keywords.REF, _prefixes[col.Namespace] + ":" + col.EncodedColumnName);
                    if (col.Table.Namespace != _ds!.Namespace)
                    {
                        _ = GetSchema(col.Table.Namespace);
                    }
                }
            }


            minOccurs = (col.AllowDBNull) ? 0 : 1;


            // March 2001 change
            if (col.ColumnMapping == MappingType.Attribute && minOccurs != 0)
                root.SetAttribute(Keywords.USE, Keywords.REQUIRED);


            if (col.ColumnMapping == MappingType.Hidden)
            {
                root.SetAttribute(Keywords.USE, Keywords.PROHIBITED);
            }
            else
                if (col.ColumnMapping != MappingType.Attribute && minOccurs != 1)
                root.SetAttribute(Keywords.MINOCCURS, minOccurs.ToString(CultureInfo.InvariantCulture));

            if ((col.ColumnMapping == MappingType.Element) && fWriteOrdinal)
                root.SetAttribute(Keywords.MSD_ORDINAL, Keywords.MSDNS, col.Ordinal.ToString(CultureInfo.InvariantCulture));

            return root;
        }


        internal static string TranslateAcceptRejectRule(AcceptRejectRule rule) =>
            rule switch
            {
                AcceptRejectRule.Cascade => "Cascade",
                AcceptRejectRule.None => "None",
                _ => null!,
            };

        internal static string TranslateRule(Rule rule) =>
            rule switch
            {
                Rule.Cascade => "Cascade",
                Rule.None => "None",
                Rule.SetNull => "SetNull",
                Rule.SetDefault => "SetDefault",
                _ => null!,
            };

        internal void AppendChildWithoutRef(string Namespace, XmlElement el)
        {
            XmlElement schNode = GetSchema(Namespace);
            if (FindTypeNode(schNode, el.GetAttribute(Keywords.NAME)) == null)
                schNode.AppendChild(el);
        }

        internal static XmlElement? FindTypeNode(XmlElement node, string strType)
        {
            if (node == null)
                return null;

            for (XmlNode? n = node.FirstChild; n != null; n = n.NextSibling)
            {
                if (!(n is XmlElement))
                    continue;

                XmlElement child = (XmlElement)n;

                if (XSDSchema.FEqualIdentity(child, Keywords.XSD_ELEMENT, Keywords.XSDNS) ||
                    XSDSchema.FEqualIdentity(child, Keywords.XSD_ATTRIBUTE, Keywords.XSDNS) ||
                    XSDSchema.FEqualIdentity(child, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS) ||
                    XSDSchema.FEqualIdentity(child, Keywords.XSD_SIMPLETYPE, Keywords.XSDNS))
                {
                    if (child.GetAttribute(Keywords.NAME) == strType)
                        return child;
                }
            }
            return null;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal XmlElement HandleTable(DataTable table, XmlDocument dc, XmlElement schema)
        {
            return HandleTable(table, dc, schema, true);
        }

        // we write out column Ordinals only if the table contains columns
        // that map both to Attributes and Elements

        private static bool HasMixedColumns(DataTable table)
        {
            bool hasAttributes = false;
            bool hasElements = false;
            foreach (DataColumn col in table.Columns)
            {
                if (!hasElements && col.ColumnMapping == MappingType.Element)
                    hasElements = true;
                if (!hasAttributes && (col.ColumnMapping == MappingType.Attribute || col.ColumnMapping == MappingType.Hidden))
                    hasAttributes = !AutoGenerated(col);
                if (hasAttributes && hasElements)
                    return true;
            }
            return false;
        }

        internal static bool AutoGenerated(DataColumn col)
        {
            // for now we use just this simple logic for the columns.

            if (col.ColumnMapping != MappingType.Hidden)
                return false;

            if (col.DataType != typeof(int))
                return false;

            string generatedname = col.Table!.TableName + "_Id";

            if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname + "_0"))
                return true;

            generatedname = string.Empty;

            foreach (DataRelation rel in col.Table.ParentRelations)
            {
                if (!rel.Nested)
                    continue;
                if (rel.ChildColumnsReference.Length != 1)
                    continue;

                if (rel.ChildColumnsReference[0] != col)
                    continue;

                if (rel.ParentColumnsReference.Length != 1)
                    continue;

                //ok if we are here it means that we have a 1column-1column relation
                generatedname = rel.ParentColumnsReference[0].Table!.TableName + "_Id";
            }

            if ((col.ColumnName == generatedname) || (col.ColumnName == generatedname + "_0"))
                return true;

            return false;
        }
        internal static bool AutoGenerated(DataRelation rel)
        {
            string rName = rel.ParentTable.TableName + "_" + rel.ChildTable.TableName;
            if (!rel.RelationName.StartsWith(rName, StringComparison.Ordinal))
                return false;
            if (rel.ExtendedProperties.Count > 0)
                return false;
            return true;
        }
        internal static bool AutoGenerated(UniqueConstraint unique)
        {
            // for now we use just this simple logic for the columns.
            if (!unique.ConstraintName.StartsWith("Constraint", StringComparison.Ordinal))
                return false;
            if (unique.Key.ColumnsReference.Length != 1)
                return false;
            if (unique.ExtendedProperties.Count > 0)
                return false;
            return AutoGenerated(unique.Key.ColumnsReference[0]);
        }

        private static bool AutoGenerated(ForeignKeyConstraint fk)
        {
            return AutoGenerated(fk, true);
        }

        internal static bool AutoGenerated(ForeignKeyConstraint fk, bool checkRelation)
        {
            // for now we use just this simple logic for the columns.
            DataRelation? rel = fk.FindParentRelation();
            if (checkRelation)
            {
                if (rel == null)
                    return false; // otherwise roundtrip will create column

                if (!AutoGenerated(rel))
                    return false;

                if (rel.RelationName != fk.ConstraintName)
                    return false;
            }

            if (fk.ExtendedProperties.Count > 0)
                return false;

            if (fk.AcceptRejectRule != AcceptRejectRule.None)
                return false;
            if (fk.DeleteRule != Rule.Cascade)
                return false;

            if (fk.RelatedColumnsReference.Length != 1)
                return false;
            return AutoGenerated(fk.RelatedColumnsReference[0]);
        }

        private bool IsAutoGenerated(object o)
        {
            if (_schFormat != SchemaFormat.Remoting)
                return _autogenerated[o] != null;
            return false;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal XmlElement HandleTable(DataTable table, XmlDocument dc, XmlElement schema, bool genNested)
        {
            Debug.Assert(_prefixes != null);
            Debug.Assert(_dc != null);
            Debug.Assert(_dsElement != null);

            XmlElement root = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
            bool fWriteOrdinals;
            bool fUnqualified = false;

            if (((table.DataSet == null) || (_ds != null && table.Namespace != _ds.Namespace)) && (_schFormat == SchemaFormat.Remoting))
                root.SetAttribute(Keywords.TARGETNAMESPACE, Keywords.MSDNS, table.Namespace);

            // First add any attributes.
            root.SetAttribute(Keywords.NAME, table.EncodedTableName);

            if (table.Namespace.Length == 0)
            {
                DataTable _table = table;
                string tgNamespace = _table.Namespace;
                while (string.IsNullOrEmpty(tgNamespace))
                {
                    DataRelation[] nestedParentRelations = _table.NestedParentRelations;
                    if (nestedParentRelations.Length == 0)
                    {
                        tgNamespace = (_ds != null) ? _ds.Namespace : "";
                        break;
                    }
                    int nestedparentPosition = -1; // it is find non-self-nested-parent
                    for (int i = 0; i < nestedParentRelations.Length; i++)
                    {
                        if (nestedParentRelations[i].ParentTable != _table)
                        {
                            nestedparentPosition = i;
                            break;
                        }
                    }
                    if (nestedparentPosition == -1)
                    {
                        break;
                    }
                    else
                    {
                        _table = nestedParentRelations[nestedparentPosition].ParentTable;
                    }
                    tgNamespace = _table.Namespace;
                }

                if (table.Namespace != tgNamespace)
                {
                    root.SetAttribute(Keywords.FORM, Keywords.UNQUALIFIED);
                    fUnqualified = true;
                }
            }


            if (table.ShouldSerializeCaseSensitive())
            {
                root.SetAttribute(Keywords.MSD_CASESENSITIVE, Keywords.MSDNS, table.CaseSensitive.ToString());
            }
            if (table.ShouldSerializeLocale())
            {
                root.SetAttribute(Keywords.MSD_LOCALE, Keywords.MSDNS, table.Locale.ToString());
            }

            AddXdoProperties(table, root);

            DataColumnCollection columns = table.Columns;

            int cCount = columns.Count;
            int realCount = 0;

            if (cCount == 1 || cCount == 2)
                for (int i = 0; i < cCount; i++)
                {
                    DataColumn col = columns[i];

                    if (col.ColumnMapping == MappingType.Hidden)
                    {
                        DataRelationCollection childRelations = table.ChildRelations;
                        for (int j = 0; j < childRelations.Count; j++)
                        {
                            if (childRelations[j].Nested && childRelations[j].ParentKey.ColumnsReference.Length == 1 && childRelations[j].ParentKey.ColumnsReference[0] == col)
                                realCount++;
                        }
                    }

                    if (col.ColumnMapping == MappingType.Element)
                        realCount++;
                }


            if ((table._repeatableElement) && (realCount == 1))
            {
                // I only have 1 column and that gives me
                // the type for this element
                DataColumn col = table.Columns[0];
                string _typeName = XmlDataTypeName(col.DataType);
                if (string.IsNullOrEmpty(_typeName))
                {
                    _typeName = Keywords.XSD_ANYTYPE;
                }

                root.SetAttribute(Keywords.TYPE, XSDSchema.QualifiedName(_typeName));
                return root;
            }

            // Now add the type information nested inside the element or global.
            XmlElement type = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_COMPLEXTYPE, Keywords.XSDNS);

            if (!table.TypeName.IsEmpty && _schFormat != SchemaFormat.Remoting)
            {
                XmlElement typeSchema = GetSchema(table.TypeName.Namespace);
                if (string.IsNullOrEmpty(table.TypeName.Namespace))
                {
                    if (_ds == null)
                        typeSchema = GetSchema(table.Namespace);
                    else
                        typeSchema = fUnqualified ? GetSchema(_ds.Namespace) : GetSchema(table.Namespace);
                }

                if (FindTypeNode(typeSchema, table.TypeName.Name) == null)
                    typeSchema.AppendChild(type);

                type.SetAttribute(Keywords.NAME, table.TypeName.Name);
            }
            else
            {
                root.AppendChild(type);
            }

            if (!table.TypeName.IsEmpty)
            {
                if (_schFormat != SchemaFormat.Remoting)
                    root.SetAttribute(Keywords.TYPE, NewDiffgramGen.QualifiedName((string)_prefixes[table.TypeName.Namespace]!, table.TypeName.Name));
            }

            XmlElement? compositor;

            DataColumn? colTxt = table.XmlText;

            if (colTxt != null)
            {
                XmlElement sc = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SIMPLECONTENT, Keywords.XSDNS);

                if (colTxt.GetType() != typeof(DataColumn))
                    AddXdoProperties(colTxt, sc);
                else
                    AddColumnProperties(colTxt, sc);
                AddExtendedProperties(colTxt._extendedProperties, sc);

                if (colTxt.AllowDBNull)
                    root.SetAttribute(Keywords.XSD_NILLABLE, string.Empty, Keywords.TRUE);
                if (!colTxt.DefaultValueIsNull)
                {
                    XmlTreeGen.ValidateColumnMapping(colTxt.DataType);
                    sc.SetAttribute(Keywords.MSD_DEFAULTVALUE, Keywords.MSDNS, colTxt.ConvertObjectToXml(colTxt.DefaultValue));
                }

                sc.SetAttribute(Keywords.MSD_COLUMNNAME, Keywords.MSDNS, colTxt.ColumnName);
                sc.SetAttribute(Keywords.MSD_ORDINAL, Keywords.MSDNS, colTxt.Ordinal.ToString(CultureInfo.InvariantCulture));

                type.AppendChild(sc);
                XmlElement ext = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_EXTENSION, Keywords.XSDNS);
                sc.AppendChild(ext);
                HandleColumnType(colTxt, dc, ext, schema);
                type = ext;
            }

            compositor = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SEQUENCE, Keywords.XSDNS);
            type.AppendChild(compositor);

            fWriteOrdinals = HasMixedColumns(table);

            for (int i = 0; i < cCount; i++)
            {
                DataColumn col = columns[i];

                if (col.ColumnMapping == MappingType.SimpleContent)
                    continue;

                if (col.ColumnMapping == MappingType.Attribute || col.ColumnMapping == MappingType.Element || col.ColumnMapping == MappingType.Hidden)
                {
                    if (IsAutoGenerated(col)) // skip automanifactured columns
                        continue;
                    bool isAttribute = col.ColumnMapping != MappingType.Element;
                    XmlElement el = HandleColumn(col, dc, schema, fWriteOrdinals);

                    XmlElement node = isAttribute ? type : compositor;
                    //bool flag = isAttribute ? col.Namespace == "" : col.Namespace == table.Namespace;
                    node.AppendChild(el);
                }
            }

            if ((table.XmlText == null) && (genNested))
            {
                DataRelationCollection childRelations = table.ChildRelations;

                for (int j = 0; j < childRelations.Count; j++)
                {
                    XmlElement NestedTable;

                    if (!childRelations[j].Nested)
                        continue;

                    DataTable childTable = childRelations[j].ChildTable;

                    if (childTable == table)
                    { // self join
                        NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);

                        NestedTable.SetAttribute(Keywords.REF, table.EncodedTableName);
                    }
                    else if (childTable.NestedParentsCount > 1)
                    { // skip relations with multiple parents
                        NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
                        NestedTable.SetAttribute(Keywords.REF, childTable.EncodedTableName);
                    }
                    else
                        NestedTable = HandleTable(childTable, dc, schema);

                    if (childTable.Namespace == table.Namespace)
                    {
                        NestedTable.SetAttribute(Keywords.MINOCCURS, "0");
                        NestedTable.SetAttribute(Keywords.MAXOCCURS, Keywords.ZERO_OR_MORE);
                    }

                    if ((childTable.Namespace == table.Namespace) || (childTable.Namespace.Length == 0) || _schFormat == SchemaFormat.Remoting)
                    {
                        compositor.AppendChild(NestedTable);
                    }
                    else
                    {
                        if (childTable.NestedParentsCount <= 1)
                            GetSchema(childTable.Namespace).AppendChild(NestedTable);
                        NestedTable = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ELEMENT, Keywords.XSDNS);
                        NestedTable.SetAttribute(Keywords.REF, ((string)_prefixes[childTable.Namespace]!) + ':' + childTable.EncodedTableName);
                        compositor.AppendChild(NestedTable);
                    }

                    if (childRelations[j].ChildKeyConstraint != null)
                        continue; // we write the relation using the constraint

                    XmlElement nodeAnn = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_ANNOTATION, Keywords.XSDNS);
                    NestedTable.PrependChild(nodeAnn);

                    XmlElement nodeApp = _dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_APPINFO, Keywords.XSDNS);
                    nodeAnn.AppendChild(nodeApp);

                    nodeApp.AppendChild(HandleRelation(childRelations[j], dc));
                }
            }

            if (compositor != null)
                if (!compositor.HasChildNodes)
                    type.RemoveChild(compositor);

            // Output all constraints.


            ConstraintCollection constraints = table.Constraints;
            XmlElement selector, field;
            string xpathprefix = (_ds != null) ? (_ds.Namespace.Length != 0 ? Keywords.MSTNS_PREFIX : string.Empty) : string.Empty;
            if (_schFormat != SchemaFormat.Remoting)
            {
                GetSchema(table.Namespace); // to ensure prefix handling
                xpathprefix = table.Namespace.Length != 0 ? (string)_prefixes[table.Namespace]! + ':' : string.Empty;
            }

            for (int i = 0; i < constraints.Count; i++)
            {
                XmlElement? constraint;
                DataColumn[] fields;

                if (constraints[i] is UniqueConstraint unique)
                {
                    if (IsAutoGenerated(unique))
                        continue;

                    // special case of the ghosted constraints:
                    fields = unique.Key.ColumnsReference;


                    constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_UNIQUE, Keywords.XSDNS);

                    if ((_ds == null) || (_ds.Tables.InternalIndexOf(table.TableName) == -3))
                        constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, table.Namespace);
                    // convert constraint name to valid xml name
                    constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(unique.SchemaName));

                    if (unique.ConstraintName != unique.SchemaName)
                        constraint.SetAttribute(Keywords.MSD_CONSTRAINTNAME, Keywords.MSDNS, unique.ConstraintName);

                    AddExtendedProperties(unique._extendedProperties, constraint);


                    selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
                    selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + table.EncodedTableName);

                    constraint.AppendChild(selector);


                    if (unique.IsPrimaryKey)
                    {
                        constraint.SetAttribute(Keywords.MSD_PRIMARYKEY, Keywords.MSDNS, Keywords.TRUE);
                    }

                    if (0 < fields.Length)
                    {
                        StringBuilder encodedName = new StringBuilder();
                        for (int k = 0; k < fields.Length; k++)
                        {
                            encodedName.Length = 0;

                            if (_schFormat != SchemaFormat.Remoting)
                            {
                                GetSchema(fields[k].Namespace);
                                if (!string.IsNullOrEmpty(fields[k].Namespace))
                                {
                                    encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
                                }
                                encodedName.Append(fields[k].EncodedColumnName);
                            }
                            else
                            {
                                encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
                            }
                            if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
                            {
                                encodedName.Insert(0, '@');
                            }
                            field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
                            field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());

                            constraint.AppendChild(field);
                        }
                    }

                    _dsElement.InsertBefore(constraint, _constraintSeparator);
                }
                else if (constraints[i] is ForeignKeyConstraint && genNested)
                {
                    ForeignKeyConstraint foreign = (ForeignKeyConstraint)constraints[i];

                    if (_tables.Count > 0)
                    {
                        if (!_tables.Contains(foreign.RelatedTable) || !_tables.Contains(foreign.Table))
                            continue;
                    }

                    if (IsAutoGenerated(foreign))
                        continue;


                    DataRelation? rel = foreign.FindParentRelation();

                    // special case of the ghosted constraints:
                    fields = foreign.RelatedColumnsReference;


                    UniqueConstraint? _constraint = (UniqueConstraint?)foreign.RelatedTable.Constraints.FindConstraint(new UniqueConstraint("TEMP", fields));

                    if (_constraint == null)
                    {
                        constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_KEY, Keywords.XSDNS);
                        constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(foreign.SchemaName));

                        if ((_ds == null) || (_ds.Tables.InternalIndexOf(table.TableName) == -3))
                            constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, table.Namespace);

                        selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
                        selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + foreign.RelatedTable.EncodedTableName);

                        constraint.AppendChild(selector);

                        if (0 < fields.Length)
                        {
                            StringBuilder encodedName = new StringBuilder();
                            for (int k = 0; k < fields.Length; k++)
                            {
                                encodedName.Length = 0;

                                if (_schFormat != SchemaFormat.Remoting)
                                {
                                    GetSchema(fields[k].Namespace);
                                    if (!string.IsNullOrEmpty(fields[k].Namespace))
                                    {
                                        encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
                                    }
                                    encodedName.Append(fields[k].EncodedColumnName);
                                }
                                else
                                {
                                    encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
                                }
                                if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
                                {
                                    encodedName.Insert(0, '@');
                                }
                                field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
                                field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());

                                constraint.AppendChild(field);
                            }
                        }

                        _dsElement.InsertBefore(constraint, _constraintSeparator);
                    }

                    constraint = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_KEYREF, Keywords.XSDNS);
                    // convert constraint name to valid xml name
                    constraint.SetAttribute(Keywords.NAME, XmlConvert.EncodeLocalName(foreign.SchemaName));

                    if ((_ds == null) || (_ds.Tables.InternalIndexOf(foreign.RelatedTable.TableName) == -3)) // if there is a conflicting name/namespace only
                        constraint.SetAttribute(Keywords.MSD_TABLENS, Keywords.MSDNS, foreign.Table!.Namespace);

                    if (_constraint == null)
                        constraint.SetAttribute(Keywords.REFER, XmlConvert.EncodeLocalName(foreign.SchemaName));
                    else
                        constraint.SetAttribute(Keywords.REFER, XmlConvert.EncodeLocalName(_constraint.SchemaName));

                    AddExtendedProperties(foreign._extendedProperties, constraint, typeof(ForeignKeyConstraint));

                    if (foreign.ConstraintName != foreign.SchemaName)
                        constraint.SetAttribute(Keywords.MSD_CONSTRAINTNAME, Keywords.MSDNS, foreign.ConstraintName);

                    if (null == rel)
                    {
                        constraint.SetAttribute(Keywords.MSD_CONSTRAINTONLY, Keywords.MSDNS, Keywords.TRUE);
                    }
                    else
                    {
                        if (rel.Nested)
                            constraint.SetAttribute(Keywords.MSD_ISNESTED, Keywords.MSDNS, Keywords.TRUE);

                        AddExtendedProperties(rel._extendedProperties, constraint, typeof(DataRelation));
                        if (foreign.ConstraintName != rel.RelationName)
                        {
                            constraint.SetAttribute(Keywords.MSD_RELATIONNAME, Keywords.MSDNS, XmlConvert.EncodeLocalName(rel.RelationName));
                        }
                    }

                    selector = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_SELECTOR, Keywords.XSDNS);
                    selector.SetAttribute(Keywords.XSD_XPATH, ".//" + xpathprefix + table.EncodedTableName);

                    constraint.AppendChild(selector);

                    if (foreign.AcceptRejectRule != ForeignKeyConstraint.AcceptRejectRule_Default)
                        constraint.SetAttribute(Keywords.MSD_ACCEPTREJECTRULE, Keywords.MSDNS,
                                                TranslateAcceptRejectRule(foreign.AcceptRejectRule));

                    if (foreign.UpdateRule != ForeignKeyConstraint.Rule_Default)
                        constraint.SetAttribute(Keywords.MSD_UPDATERULE, Keywords.MSDNS, TranslateRule(foreign.UpdateRule));

                    if (foreign.DeleteRule != ForeignKeyConstraint.Rule_Default)
                        constraint.SetAttribute(Keywords.MSD_DELETERULE, Keywords.MSDNS, TranslateRule(foreign.DeleteRule));

                    fields = foreign.Columns;

                    if (0 < fields.Length)
                    {
                        StringBuilder encodedName = new StringBuilder();
                        for (int k = 0; k < fields.Length; k++)
                        {
                            encodedName.Length = 0;

                            if (_schFormat != SchemaFormat.Remoting)
                            {
                                GetSchema(fields[k].Namespace);
                                if (!string.IsNullOrEmpty(fields[k].Namespace))
                                {
                                    encodedName.Append(_prefixes[fields[k].Namespace]).Append(':');
                                }
                                encodedName.Append(fields[k].EncodedColumnName);
                            }
                            else
                            {
                                encodedName.Append(xpathprefix).Append(fields[k].EncodedColumnName);
                            }
                            if ((fields[k].ColumnMapping == MappingType.Attribute) || (fields[k].ColumnMapping == MappingType.Hidden))
                            {
                                encodedName.Insert(0, '@');
                            }
                            field = dc.CreateElement(Keywords.XSD_PREFIX, Keywords.XSD_FIELD, Keywords.XSDNS);
                            field.SetAttribute(Keywords.XSD_XPATH, encodedName.ToString());

                            constraint.AppendChild(field);
                        }
                    }

                    _dsElement.InsertAfter(constraint, _constraintSeparator);
                }
            }

            AddExtendedProperties(table._extendedProperties, root);
            return root;
        }

        /// <summary>
        /// resolve the name from the type for schema output and set the msdata:Data attribute
        /// </summary>
        /// <param name="root"></param>
        /// <param name="type">non-special type to resolve</param>
        /// <exception cref="DataException">if multipleTargetConverter throws or returns an empty result</exception>
        private void SetMSDataAttribute(XmlElement root, Type type)
        {
            string result = DataStorage.GetQualifiedName(type);
            try
            {
                if (null != _targetConverter)
                {
                    result = _targetConverter(type);
                }

                if (!string.IsNullOrEmpty(result))
                {
                    // SetAttribute doesn't fail with invalid data, but the final XmlDocument.Save will fail later
                    // with the ArgumentException when calling the actual XmlWriter.SetAttribute
                    root.SetAttribute(Keywords.MSD_DATATYPE, Keywords.MSDNS, result);
                }
            }
            catch (Exception ex) when (ADP.IsCatchableExceptionType(ex))
            {
                ExceptionBuilder.ThrowMultipleTargetConverter(ex);
            }

            if (string.IsNullOrEmpty(result))
            {
                ExceptionBuilder.ThrowMultipleTargetConverter(null);
            }
        }
    }



    internal sealed class NewDiffgramGen
    {
        internal XmlDocument _doc;
        internal DataSet? _ds;
        internal DataTable? _dt;
        internal XmlWriter _xmlw = default!; // Late-initialized
        private bool _fBefore;
        private bool _fErrors;
        internal Hashtable _rowsOrder;
        private readonly ArrayList _tables = new ArrayList();
        private readonly bool _writeHierarchy;


        internal NewDiffgramGen(DataSet ds)
        {
            _ds = ds;
            _dt = null;
            _doc = new XmlDocument();

            for (int i = 0; i < ds.Tables.Count; i++)
            {
                _tables.Add(ds.Tables[i]);
            }
            DoAssignments(_tables);
        }

        internal NewDiffgramGen(DataTable dt, bool writeHierarchy)
        {
            _ds = null;
            _dt = dt;
            _doc = new XmlDocument();
            _tables.Add(dt);
            if (writeHierarchy)
            {
                _writeHierarchy = true;
                CreateTableHierarchy(dt);
            }

            DoAssignments(_tables);
        }

        private void CreateTableHierarchy(DataTable dt)
        {
            //            if (!dt.SerializeHierarchy)
            //                return;
            foreach (DataRelation r in dt.ChildRelations)
            {
                if (!_tables.Contains(r.ChildTable))
                {
                    _tables.Add(r.ChildTable);
                    CreateTableHierarchy(r.ChildTable);
                }
            }
        }

        [MemberNotNull(nameof(_rowsOrder))]
        private void DoAssignments(ArrayList tables)
        {
            int rows = 0;
            for (int i = 0; i < tables.Count; i++)
            {
                rows += ((DataTable)tables[i]!).Rows.Count;
            }
            _rowsOrder = new Hashtable(rows);
            for (int i = 0; i < tables.Count; i++)
            {
                DataTable dt = (DataTable)tables[i]!;
                DataRowCollection rc = dt.Rows;
                rows = rc.Count;
                for (int j = 0; j < rows; j++)
                    _rowsOrder[rc[j]] = j;
            }
        }

        private bool EmptyData()
        {
            for (int i = 0; i < _tables.Count; i++)
            {
                if (((DataTable)_tables[i]!).Rows.Count > 0)
                    return false;
            }
            return true;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(XmlWriter xmlw)
        {
            Save(xmlw, null);
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(XmlWriter xmlw, DataTable? table)
        {
            _xmlw = DataTextWriter.CreateWriter(xmlw);

            _xmlw.WriteStartElement(Keywords.DFF, Keywords.DIFFGRAM, Keywords.DFFNS);
            _xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.MSD, null, Keywords.MSDNS);
            //            _xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.UPDG, null, Keywords.UPDGNS);

            if (!EmptyData())
            {
                // write the datapart
                if (table != null)
                    new XmlDataTreeWriter(table, _writeHierarchy).SaveDiffgramData(_xmlw, _rowsOrder);
                else
                    new XmlDataTreeWriter(_ds!).SaveDiffgramData(_xmlw, _rowsOrder);

                // Walk the xd using relational apis and create nodes in nodeRoot appropriately.

                if (table == null)
                {
                    for (int i = 0; i < _ds!.Tables.Count; ++i)
                    {
                        GenerateTable(_ds.Tables[i]);
                    }
                }
                else
                {
                    for (int i = 0; i < _tables.Count; i++)
                    {
                        GenerateTable((DataTable)_tables[i]!);
                    }
                }

                if (_fBefore)
                    _xmlw.WriteEndElement();  //SQL_BEFORE

                if (table == null)
                {
                    for (int i = 0; i < _ds!.Tables.Count; ++i)
                    {
                        GenerateTableErrors(_ds.Tables[i]);
                    }
                }
                else
                {
                    for (int i = 0; i < _tables.Count; i++)
                    {
                        GenerateTableErrors((DataTable)_tables[i]!);
                    }
                }

                if (_fErrors)
                    _xmlw.WriteEndElement();  //ERRORS
            }

            _xmlw.WriteEndElement();
            _xmlw.Flush();
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        private void GenerateTable(DataTable table)
        {
            int rowCount = table.Rows.Count;

            if (rowCount <= 0)
                return;

            for (int rowNum = 0; rowNum < rowCount; ++rowNum)
                GenerateRow(table.Rows[rowNum]);
        }


        private void GenerateTableErrors(DataTable table)
        {
            int rowCount = table.Rows.Count;
            int colCount = table.Columns.Count;

            if (rowCount <= 0)
                return;

            for (int rowNum = 0; rowNum < rowCount; ++rowNum)
            {
                bool tableName = false;

                DataRow row = table.Rows[rowNum];
                string prefix = (table.Namespace.Length != 0) ? table.Prefix : string.Empty;
                if ((row.HasErrors) && (row.RowError.Length > 0))
                {
                    if (!_fErrors)
                    {
                        _xmlw.WriteStartElement(Keywords.DFF, Keywords.MSD_ERRORS, Keywords.DFFNS);
                        _fErrors = true;
                    }
                    _xmlw.WriteStartElement(prefix, row.Table.EncodedTableName, row.Table.Namespace);
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.MSD_ERROR, Keywords.DFFNS, row.RowError);
                    tableName = true;
                }
                if (colCount <= 0)
                    continue;
                for (int colNum = 0; colNum < colCount; ++colNum)
                {
                    DataColumn column = table.Columns[colNum];
                    string error = row.GetColumnError(column);
                    string columnPrefix = (column.Namespace.Length != 0) ? column.Prefix : string.Empty;
                    if (string.IsNullOrEmpty(error))
                    {
                        continue;
                    }

                    if (!tableName)
                    {
                        if (!_fErrors)
                        {
                            _xmlw.WriteStartElement(Keywords.DFF, Keywords.MSD_ERRORS, Keywords.DFFNS);
                            _fErrors = true;
                        }

                        _xmlw.WriteStartElement(prefix, row.Table.EncodedTableName, row.Table.Namespace);
                        _xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));
                        tableName = true;
                    }


                    _xmlw.WriteStartElement(columnPrefix, column.EncodedColumnName, column.Namespace);
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.MSD_ERROR, Keywords.DFFNS, error);

                    _xmlw.WriteEndElement();
                }

                if (tableName)
                    _xmlw.WriteEndElement();
            }
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        private void GenerateRow(DataRow row)
        {
            DataRowState state = row.RowState;
            if ((state == DataRowState.Unchanged) || (state == DataRowState.Added))
            {
                return;
            }
            if (!_fBefore)
            {
                _xmlw.WriteStartElement(Keywords.DFF, Keywords.SQL_BEFORE, Keywords.DFFNS);
                _fBefore = true;
            }

            DataTable table = row.Table;
            int colCount = table.Columns.Count;
            string rowIDString = table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture);
            string? parentId = null;
            if ((state == DataRowState.Deleted) && (row.Table.NestedParentRelations.Length != 0))
            {
                DataRow? parentRow = row.GetNestedParentRow(DataRowVersion.Original);
                if (parentRow != null)
                {
                    parentId = parentRow.Table.TableName + parentRow.rowID.ToString(CultureInfo.InvariantCulture);
                }
            }


            string tablePrefix = (table.Namespace.Length != 0) ? table.Prefix : string.Empty;

            //old row
            _xmlw.WriteStartElement(tablePrefix, row.Table.EncodedTableName, row.Table.Namespace);

            _xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, rowIDString);

            if ((state == DataRowState.Deleted) && XmlDataTreeWriter.RowHasErrors(row))
                _xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASERRORS, Keywords.DFFNS, Keywords.TRUE);

            if (parentId != null)
                _xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFPID, Keywords.DFFNS, parentId);

            _xmlw.WriteAttributeString(Keywords.MSD, Keywords.ROWORDER, Keywords.MSDNS, _rowsOrder[row]!.ToString());
            for (int colNum = 0; colNum < colCount; ++colNum)
            {
                if ((row.Table.Columns[colNum].ColumnMapping == MappingType.Attribute) ||
                    (row.Table.Columns[colNum].ColumnMapping == MappingType.Hidden))
                    GenerateColumn(row, row.Table.Columns[colNum], DataRowVersion.Original);
            }
            for (int colNum = 0; colNum < colCount; ++colNum)
            {
                if ((row.Table.Columns[colNum].ColumnMapping == MappingType.Element) ||
                    (row.Table.Columns[colNum].ColumnMapping == MappingType.SimpleContent))
                    GenerateColumn(row, row.Table.Columns[colNum], DataRowVersion.Original);
            }
            _xmlw.WriteEndElement();  //old row
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        private void GenerateColumn(DataRow row, DataColumn col, DataRowVersion version)
        {
            string? value;

            value = col.GetColumnValueAsString(row, version); // this is useless for CTD
            if (value == null)
            {
                if (col.ColumnMapping == MappingType.SimpleContent)
                    _xmlw.WriteAttributeString(Keywords.XSI, Keywords.XSI_NIL, Keywords.XSINS, Keywords.TRUE);
                return;
            }

            string colPrefix = (col.Namespace.Length != 0) ? col.Prefix : string.Empty;
            switch (col.ColumnMapping)
            {
                case MappingType.Attribute:
                    _xmlw.WriteAttributeString(colPrefix, col.EncodedColumnName, col.Namespace, value);
                    break;

                case MappingType.Hidden:
                    _xmlw.WriteAttributeString(Keywords.MSD, "hidden" + col.EncodedColumnName, Keywords.MSDNS, value);
                    break;

                case MappingType.SimpleContent:
                    _xmlw.WriteString(value);
                    break;

                case MappingType.Element:
                    bool startElementSkipped = true;
                    object columnValue = row[col, version];
                    // if the object is built in type or if it implements IXMLSerializable, write the start Element, otherwise
                    //(if CDT and does not implement IXmlSerializable) skip it
                    if (!col.IsCustomType || !DataColumn.IsValueCustomTypeInstance(columnValue) || (typeof(IXmlSerializable).IsAssignableFrom(columnValue.GetType())))
                    {
                        _xmlw.WriteStartElement(colPrefix, col.EncodedColumnName, col.Namespace);
                        startElementSkipped = false;
                    }
                    Type valuesType = columnValue.GetType();
                    if (!col.IsCustomType)
                    { // if column's type is built in type CLR or SQLType
                        if (valuesType == typeof(char) || valuesType == typeof(string))
                        {
                            if (XmlDataTreeWriter.PreserveSpace(value))
                            {
                                _xmlw.WriteAttributeString(Keywords.XML, Keywords.SPACE, Keywords.XML_XMLNS, Keywords.PRESERVE);
                            }
                        }
                        _xmlw.WriteString(value);
                    }
                    else
                    { // Columns type is CDT
                        if ((columnValue != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(columnValue)))
                        {
                            if (DataColumn.IsValueCustomTypeInstance(columnValue)/* && valuesType != typeof(Type)*/)
                            {// value is also CDT
                                // if SkippedElement, ie does not implement IXMLSerializable: so No Polymorphysm Support.
                                if (!startElementSkipped && columnValue.GetType() != col.DataType)
                                {
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, DataStorage.GetQualifiedName(valuesType));
                                }
                                if (!startElementSkipped)
                                {
                                    col.ConvertObjectToXml(columnValue, _xmlw, null); // XmlRootAttribute MUST be passed null
                                }
                                else
                                {
                                    // this column's type does not implement IXmlSerializable, so we need to handle serialization via XmlSerializer
                                    if (columnValue.GetType() != col.DataType)
                                    { // throw if polymorphism; not supported
                                        throw ExceptionBuilder.PolymorphismNotSupported(valuesType.AssemblyQualifiedName!);
                                    }
                                    // therefore we are skipping the start element, but by passing XmlRootAttribute with the same name as
                                    // we open the start element (column's name), XmlSerializer will open and close it for us
                                    XmlRootAttribute xmlAttrib = new XmlRootAttribute(col.EncodedColumnName);
                                    xmlAttrib.Namespace = col.Namespace;
                                    col.ConvertObjectToXml(columnValue, _xmlw, xmlAttrib);
                                }
                            }
                            else
                            { // value is built in CLR type (eg: string, int etc.)
                              // these basic clr types do not have direct xsd type mappings
                                if (valuesType == typeof(Type) || valuesType == typeof(Guid) || valuesType == typeof(char) ||
                                    DataStorage.IsSqlType(valuesType))
                                { // if unmapped type or SQL type write msdata:Datatype=typeofinstance
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, valuesType.FullName);
                                }
                                else if (columnValue is Type)
                                {
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, Keywords.TYPEINSTANCE);
                                }
                                else
                                {
                                    string xsdTypeName = Keywords.XSD_PREFIXCOLON + XmlTreeGen.XmlDataTypeName(valuesType);
                                    _xmlw.WriteAttributeString(Keywords.XSI, Keywords.TYPE, Keywords.XSINS, xsdTypeName);
                                    _xmlw.WriteAttributeString(Keywords.XSD_PREFIX, Keywords.XMLNS, Keywords.XSDNS, xsdTypeName);
                                }
                                if (!DataStorage.IsSqlType(valuesType))
                                {
                                    _xmlw.WriteString(col.ConvertObjectToXml(columnValue));
                                }
                                else
                                {
                                    col.ConvertObjectToXml(columnValue, _xmlw, null);
                                }
                            }
                        }
                    }

                    if (!startElementSkipped)
                    {
                        _xmlw.WriteEndElement();
                    }
                    break;
            }
        }

        internal static string QualifiedName(string prefix, string name)
        {
            if (prefix != null)
                return prefix + ":" + name;

            return name;
        }
    }

    // DataTreeWriter
    internal sealed class XmlDataTreeWriter
    {
        private XmlWriter? _xmlw;

        private readonly DataSet? _ds;
        private readonly DataTable? _dt;

        private readonly ArrayList _dTables = new ArrayList();
        private readonly DataTable[] _topLevelTables;

        private readonly bool _fFromTable; // also means no hierarchy
        private bool _isDiffgram;
        private Hashtable? _rowsOrder;
        private readonly bool _writeHierarchy;

        internal XmlDataTreeWriter(DataSet ds)
        {
            _ds = ds;
            _topLevelTables = ds.TopLevelTables();
            foreach (DataTable table in ds.Tables)
            {
                _dTables.Add(table);
            }
        }

        internal XmlDataTreeWriter(DataTable dt, bool writeHierarchy)
        {
            _dt = dt;
            _fFromTable = true;
            if (dt.DataSet == null)
            {
                _dTables.Add(dt);
                _topLevelTables = new DataTable[] { dt };
            }
            else
            {
                _ds = dt.DataSet;
                _dTables.Add(dt);
                if (writeHierarchy)
                {
                    _writeHierarchy = true;
                    CreateTablesHierarchy(dt);
                    _topLevelTables = CreateToplevelTables();
                }
                else // if no hierarchy , top level table should be dt
                    _topLevelTables = new DataTable[] { dt };
            }
        }

        private DataTable[] CreateToplevelTables()
        {
            ArrayList topTables = new ArrayList();
            for (int i = 0; i < _dTables.Count; i++)
            {
                DataTable table = (DataTable)_dTables[i]!;
                if (table.ParentRelations.Count == 0)
                    topTables.Add(table);
                else
                {
                    bool fNestedButNotSelfNested = false;
                    for (int j = 0; j < table.ParentRelations.Count; j++)
                    {
                        if (table.ParentRelations[j].Nested)
                        {
                            if (table.ParentRelations[j].ParentTable == table)
                            {
                                fNestedButNotSelfNested = false;
                                break;
                            }
                            fNestedButNotSelfNested = true;
                        }
                    }
                    if (!fNestedButNotSelfNested)
                        topTables.Add(table);
                }
            }
            if (topTables.Count == 0)
            {
                return Array.Empty<DataTable>();
            }

            var temp = new DataTable[topTables.Count];
            topTables.CopyTo(temp, 0);
            return temp;
        }

        private void CreateTablesHierarchy(DataTable dt)
        {
            //            if (!dt.SerializeHierarchy)
            //                return;
            foreach (DataRelation r in dt.ChildRelations)
            {
                if (!_dTables.Contains(r.ChildTable))
                {
                    _dTables.Add(r.ChildTable);
                    CreateTablesHierarchy(r.ChildTable);
                }
            }
        }

        internal static bool RowHasErrors(DataRow row)
        {
            int colCount = row.Table.Columns.Count;

            if ((row.HasErrors) && (row.RowError.Length > 0))
                return true;

            for (int colNum = 0; colNum < colCount; ++colNum)
            {
                DataColumn column = row.Table.Columns[colNum];
                string error = row.GetColumnError(column);
                if (string.IsNullOrEmpty(error))
                {
                    continue;
                }
                return true;
            }

            return false;
        }

        // the following line writes the data part
        // for the new diffgram format

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void SaveDiffgramData(XmlWriter xw, Hashtable rowsOrder)
        {
            Debug.Assert(_ds != null || _dt != null);

            _xmlw = DataTextWriter.CreateWriter(xw);
            _isDiffgram = true;
            _rowsOrder = rowsOrder;

            string prefix = (_ds != null) ? ((_ds.Namespace.Length == 0) ? "" : _ds.Prefix) : ((_dt!.Namespace.Length == 0) ? "" : _dt.Prefix);

            if (_ds == null || string.IsNullOrEmpty(_ds.DataSetName))
                _xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, (_dt!.Namespace == null) ? "" : _dt.Namespace);
            else
                _xmlw.WriteStartElement(prefix, XmlConvert.EncodeLocalName(_ds.DataSetName), _ds.Namespace);

            // new XmlTreeGen(true).Save(_ds,_xmlw, false /* we don't care since we specified it's serialized */);

            for (int i = 0; i < _dTables.Count; i++)
            {
                DataTable tempTable = ((DataTable)_dTables[i]!);
                foreach (DataRow row in tempTable.Rows)
                {
                    if (row.RowState == DataRowState.Deleted)
                        continue;
                    int nestedParentRowCount = row.GetNestedParentCount();
                    if (nestedParentRowCount == 0)
                    {
                        DataTable tempDT = ((DataTable)_dTables[i]!);
                        XmlDataRowWriter(row, tempDT.EncodedTableName);
                    }
                    else if (nestedParentRowCount > 1)
                    {
                        throw ExceptionBuilder.MultipleParentRows(tempTable.Namespace.Length == 0 ? tempTable.TableName : tempTable.Namespace + tempTable.TableName);
                        // At all times a nested row can only have 0 or 1 parents, never more than 1
                    }
                }
            }

            _xmlw.WriteEndElement();
            _xmlw.Flush();
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void Save(XmlWriter xw, bool writeSchema)
        {
            Debug.Assert(_ds != null || _dt != null);

            _xmlw = DataTextWriter.CreateWriter(xw);

            int countTopTable = _topLevelTables.Length;
            bool fWriteDSElement = true;

            string prefix = (_ds != null) ? ((_ds.Namespace.Length == 0) ? "" : _ds.Prefix) : ((_dt!.Namespace.Length == 0) ? "" : _dt.Prefix);

            if (!writeSchema && _ds != null && _ds._fTopLevelTable && countTopTable == 1)
            {
                if (_ds.TopLevelTables()[0].Rows.Count == 1)
                    fWriteDSElement = false;
            }

            if (fWriteDSElement)
            {
                if (_ds == null)
                {
                    _xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, _dt!.Namespace);
                }
                else
                {
                    if (string.IsNullOrEmpty(_ds.DataSetName))
                        _xmlw.WriteStartElement(prefix, Keywords.DOCUMENTELEMENT, _ds.Namespace);
                    else
                        _xmlw.WriteStartElement(prefix, XmlConvert.EncodeLocalName(_ds.DataSetName), _ds.Namespace);
                }

                for (int i = 0; i < _dTables.Count; i++)
                {
                    if (((DataTable)_dTables[i]!)._xmlText != null)
                    {
                        _xmlw.WriteAttributeString(Keywords.XMLNS, Keywords.XSI, Keywords.XSD_XMLNS_NS, Keywords.XSINS);
                        break;
                    }
                }

                if (writeSchema)
                {
                    if (!_fFromTable)
                    {
                        new XmlTreeGen(SchemaFormat.Public).Save(_ds!, _xmlw);
                    }
                    else
                    {
                        new XmlTreeGen(SchemaFormat.Public).Save(null, _dt!, _xmlw, _writeHierarchy);
                    }
                }
            }

            for (int i = 0; i < _dTables.Count; i++)
            {
                foreach (DataRow row in ((DataTable)_dTables[i]!).Rows)
                {
                    if (row.RowState == DataRowState.Deleted)
                        continue;
                    int parentRowCount = row.GetNestedParentCount();
                    if (parentRowCount == 0)
                    {
                        XmlDataRowWriter(row, ((DataTable)_dTables[i]!).EncodedTableName);
                    }
                    else if (parentRowCount > 1)
                    {
                        DataTable dt = (DataTable)_dTables[i]!;
                        throw ExceptionBuilder.MultipleParentRows(dt.Namespace.Length == 0 ? dt.TableName : (dt.Namespace + dt.TableName));
                        // At all times a nested row can only have 0 or 1 parents, never more than 1
                    }
                }
            }

            if (fWriteDSElement)
                _xmlw.WriteEndElement();
            _xmlw.Flush();
        }

        private static ArrayList GetNestedChildRelations(DataRow row)
        {
            ArrayList list = new ArrayList();

            foreach (DataRelation r in row.Table.ChildRelations)
            {
                if (r.Nested)
                    list.Add(r);
            }

            return list;
        }

        [RequiresUnreferencedCode(DataSet.RequiresUnreferencedCodeMessage)]
        [RequiresDynamicCode(DataSet.RequiresDynamicCodeMessage)]
        internal void XmlDataRowWriter(DataRow row, string encodedTableName)
        {
            Debug.Assert(_xmlw != null);

            object value;
            string prefix = (row.Table.Namespace.Length == 0) ? "" : row.Table.Prefix;

            _xmlw.WriteStartElement(prefix, encodedTableName, row.Table.Namespace);

            if (_isDiffgram)
            {
                _xmlw.WriteAttributeString(Keywords.DFF, Keywords.DIFFID, Keywords.DFFNS, row.Table.TableName + row.rowID.ToString(CultureInfo.InvariantCulture));

                _xmlw.WriteAttributeString(Keywords.MSD, Keywords.ROWORDER, Keywords.MSDNS, _rowsOrder![row]!.ToString());

                if (row.RowState == DataRowState.Added)
                {
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASCHANGES, Keywords.DFFNS, Keywords.INSERTED);
                }
                if (row.RowState == DataRowState.Modified)
                {
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASCHANGES, Keywords.DFFNS, Keywords.MODIFIED);
                }

                if (RowHasErrors(row))
                {
                    _xmlw.WriteAttributeString(Keywords.DFF, Keywords.HASERRORS, Keywords.DFFNS, Keywords.TRUE);
                }
            }

            //write the attribute columns first, if any
            foreach (DataColumn col in row.Table.Columns)
            {
                if (col._columnMapping == MappingType.Attribute)
                {
                    value = row[col];
                    string colPrefix = (col.Namespace.Length == 0) ? "" : col.Prefix;

                    if ((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value)))
                    {
                        XmlTreeGen.ValidateColumnMapping(col.DataType);
                        _xmlw.WriteAttributeString(colPrefix, col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value));
                    }
                }

                if (!_isDiffgram)
                    continue;

                if (col._columnMapping == MappingType.Hidden)
                {
                    value = row[col];

                    if ((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value)))
                    {
                        XmlTreeGen.ValidateColumnMapping(col.DataType);
                        _xmlw.WriteAttributeString(Keywords.MSD, "hidden" + col.EncodedColumnName, Keywords.MSDNS, col.ConvertObjectToXml(value));
                    }
                }
            } //end foreach

            foreach (DataColumn col in row.Table.Columns)
            {
                if (col._columnMapping != MappingType.Hidden)
                {
                    value = row[col];
                    string colPrefix = (col.Namespace.Length == 0) ? "" : col.Prefix;
                    bool startElementSkipped = true;

                    if (((value == DBNull.Value) || (col.ImplementsINullable && DataStorage.IsObjectSqlNull(value))) && (col.ColumnMapping == MappingType.SimpleContent))
                        _xmlw.WriteAttributeString(Keywords.XSI, Keywords.XSI_NIL, Keywords.XSINS, Keywords.TRUE);
                    // basically this is a continue; if it is null we write xsi:nil='true'
                    // below, the check is if it is not null
                    if (((value != DBNull.Value) && (!col.ImplementsINullable || !DataStorage.IsObjectSqlNull(value))) && (col._columnMapping != MappingType.Attribute))
                    {
                        if (col._columnMapping != MappingType.SimpleContent)
                        {
                            // again, if we need to use XmlSerializer, do not write start Element (see above for more info)
                            if (!col.IsCustomType || !DataColumn.IsValueCustomTypeInstance(value) || (typeof(IXmlSerializable).IsAssignableFrom(value.GetType())))
                            {
                                _xmlw.WriteStartElement(colPrefix, col.EncodedColumnName, col.Namespace);
                                startElementSkipped = false;
                            }
                        }

                        Type valuesType = value.GetType();
                        if (!col.IsCustomType)
                        { // if column's type is built in type: CLR and SQLTypes : ie storage supported types
                            if (valuesType == typeof(char) || valuesType == typeof(string))
                            {
                                if (PreserveSpace(value))
                                {
                                    _xmlw.WriteAttributeString(Keywords.XML, Keywords.SPACE, Keywords.XML_XMLNS, Keywords.PRESERVE);
                                }
                            }
                            _xmlw.WriteString(col.ConvertObjectToXml(value));
                        }
                        else
                        { // Columns type is CDT
                            if (DataColumn.IsValueCustomTypeInstance(value) /*&& !(value is Type) && valuesType != typeof(Type)*/)
                            {// value is also CDT
                                // if SkippedElement, ie does not implement IXMLSerializable: so No Polymorphism Support.
                                if (!startElementSkipped && valuesType != col.DataType)
                                { // for polymorphism.
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, DataStorage.GetQualifiedName(valuesType));
                                }
                                if (!startElementSkipped)
                                { // make sure XmlRootAttribute is passed null as this type implement IXmlSerializable
                                    col.ConvertObjectToXml(value, _xmlw, null); // pass XmlRootAttribute as null, it also means: No XmlSerializer
                                }
                                else
                                { // startElement is skipped: this column's type does not implement IXmlSerializable, need to go via XmlSerializer
                                    if (value.GetType() != col.DataType)
                                    { // throw if polymorphism; not supported
                                        throw ExceptionBuilder.PolymorphismNotSupported(valuesType.AssemblyQualifiedName!);
                                    }
                                    // therefore we are skipping the start element, but by passing XmlRootAttribute with the same name as
                                    // we open the start element (column's name), XmlSerializer will open and close it for us
                                    XmlRootAttribute xmlAttrib = new XmlRootAttribute(col.EncodedColumnName);
                                    xmlAttrib.Namespace = col.Namespace;
                                    col.ConvertObjectToXml(value, _xmlw, xmlAttrib);
                                }
                            }
                            else
                            { // this is case that column type is object and value is CLR or SQLTypes
                                if (valuesType == typeof(Type) || valuesType == typeof(Guid) || valuesType == typeof(char) ||
                                    DataStorage.IsSqlType(valuesType))
                                { // if unmapped type or SQL type write msdata:Datatype=typeofinstance
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, valuesType.FullName);
                                }
                                else if (value is Type)
                                {
                                    _xmlw.WriteAttributeString(Keywords.MSD, Keywords.MSD_INSTANCETYPE, Keywords.MSDNS, Keywords.TYPEINSTANCE);
                                }
                                else
                                {
                                    string xsdTypeName = Keywords.XSD_PREFIXCOLON + XmlTreeGen.XmlDataTypeName(valuesType);
                                    _xmlw.WriteAttributeString(Keywords.XSI, Keywords.TYPE, Keywords.XSINS, xsdTypeName);
                                    _xmlw.WriteAttributeString(Keywords.XSD_PREFIX, Keywords.XMLNS, Keywords.XSDNS, xsdTypeName);
                                }
                                if (!DataStorage.IsSqlType(valuesType))
                                {
                                    _xmlw.WriteString(col.ConvertObjectToXml(value));
                                }
                                else
                                {
                                    col.ConvertObjectToXml(value, _xmlw, null);
                                }
                            }
                        }
                        if (col._columnMapping != MappingType.SimpleContent && !startElementSkipped)
                            _xmlw.WriteEndElement();
                    }
                }
            } //end foreach

            if (_ds != null)
                foreach (DataRelation dr in GetNestedChildRelations(row))
                {
                    foreach (DataRow r in row.GetChildRows(dr))
                    {
                        XmlDataRowWriter(r, dr.ChildTable.EncodedTableName);
                    }
                }

            _xmlw.WriteEndElement();
        }
        internal static bool PreserveSpace(object value)
        {
            Debug.Assert(value != null, "Value can not be null");
            string tempValue = value.ToString()!;
            if (tempValue.Length == 0)
            {
                return false;
            }
            for (int i = 0; i < tempValue.Length; i++)
            {
                if (!char.IsWhiteSpace(tempValue, i))
                {
                    return false;
                }
            }
            return true;
        }
    }

    internal sealed class DataTextWriter : XmlWriter
    {
        private readonly XmlWriter _xmltextWriter;

        internal static XmlWriter CreateWriter(XmlWriter xw)
        {
            return new DataTextWriter(xw);
        }

        private DataTextWriter(XmlWriter w)
        {
            _xmltextWriter = w;
        }

        internal Stream? BaseStream
        {
            get
            {
                XmlTextWriter? textWriter = _xmltextWriter as XmlTextWriter;
                if (null != textWriter)
                {
                    return textWriter.BaseStream;
                }
                return null;
            }
        }

        public override void WriteStartDocument()
        {
            _xmltextWriter.WriteStartDocument();
        }

        public override void WriteStartDocument(bool standalone)
        {
            _xmltextWriter.WriteStartDocument(standalone);
        }

        public override void WriteEndDocument()
        {
            _xmltextWriter.WriteEndDocument();
        }

        public override void WriteDocType(string name, string? pubid, string? sysid, string? subset)
        {
            _xmltextWriter.WriteDocType(name, pubid, sysid, subset);
        }

        public override void WriteStartElement(string? prefix, string localName, string? ns)
        {
            _xmltextWriter.WriteStartElement(prefix, localName, ns);
        }

        public override void WriteEndElement()
        {
            _xmltextWriter.WriteEndElement();
        }

        public override void WriteFullEndElement()
        {
            _xmltextWriter.WriteFullEndElement();
        }

        public override void WriteStartAttribute(string? prefix, string localName, string? ns)
        {
            _xmltextWriter.WriteStartAttribute(prefix, localName, ns);
        }

        public override void WriteEndAttribute()
        {
            _xmltextWriter.WriteEndAttribute();
        }

        public override void WriteCData(string? text)
        {
            _xmltextWriter.WriteCData(text);
        }

        public override void WriteComment(string? text)
        {
            _xmltextWriter.WriteComment(text);
        }

        public override void WriteProcessingInstruction(string name, string? text)
        {
            _xmltextWriter.WriteProcessingInstruction(name, text);
        }

        public override void WriteEntityRef(string name)
        {
            _xmltextWriter.WriteEntityRef(name);
        }

        public override void WriteCharEntity(char ch)
        {
            _xmltextWriter.WriteCharEntity(ch);
        }

        public override void WriteWhitespace(string? ws)
        {
            _xmltextWriter.WriteWhitespace(ws);
        }

        public override void WriteString(string? text)
        {
            _xmltextWriter.WriteString(text);
        }

        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            _xmltextWriter.WriteSurrogateCharEntity(lowChar, highChar);
        }

        public override void WriteChars(char[] buffer, int index, int count)
        {
            _xmltextWriter.WriteChars(buffer, index, count);
        }

        public override void WriteRaw(char[] buffer, int index, int count)
        {
            _xmltextWriter.WriteRaw(buffer, index, count);
        }

        public override void WriteRaw(string data)
        {
            _xmltextWriter.WriteRaw(data);
        }

        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            _xmltextWriter.WriteBase64(buffer, index, count);
        }

        public override void WriteBinHex(byte[] buffer, int index, int count)
        {
            _xmltextWriter.WriteBinHex(buffer, index, count);
        }

        public override WriteState WriteState
        {
            get
            {
                return _xmltextWriter.WriteState;
            }
        }

        public override void Close()
        {
            _xmltextWriter.Close();
        }

        public override void Flush()
        {
            _xmltextWriter.Flush();
        }

        public override void WriteName(string name)
        {
            _xmltextWriter.WriteName(name);
        }

        public override void WriteQualifiedName(string localName, string? ns)
        {
            _xmltextWriter.WriteQualifiedName(localName, ns);
        }

        public override string? LookupPrefix(string ns)
        {
            return _xmltextWriter.LookupPrefix(ns);
        }

        public override XmlSpace XmlSpace
        {
            get
            {
                return _xmltextWriter.XmlSpace;
            }
        }

        public override string? XmlLang
        {
            get
            {
                return _xmltextWriter.XmlLang;
            }
        }

        public override void WriteNmToken(string name)
        {
            _xmltextWriter.WriteNmToken(name);
        }
    }


    internal sealed class DataTextReader : XmlReader
    {
        private readonly XmlReader _xmlreader;

        internal static XmlReader CreateReader(XmlReader xr)
        {
            Debug.Assert(!(xr is DataTextReader), "XmlReader is DataTextReader");
            return new DataTextReader(xr);
        }

        private DataTextReader(XmlReader input)
        {
            _xmlreader = input;
        }

        public override XmlReaderSettings? Settings
        {
            get
            {
                return _xmlreader.Settings;
            }
        }

        public override XmlNodeType NodeType
        {
            get
            {
                return _xmlreader.NodeType;
            }
        }

        public override string Name
        {
            get
            {
                return _xmlreader.Name;
            }
        }

        public override string LocalName
        {
            get
            {
                return _xmlreader.LocalName;
            }
        }

        public override string NamespaceURI
        {
            get
            {
                return _xmlreader.NamespaceURI;
            }
        }

        public override string Prefix
        {
            get { return _xmlreader.Prefix; }
        }

        public override bool HasValue
        {
            get { return _xmlreader.HasValue; }
        }

        public override string Value
        {
            get { return _xmlreader.Value; }
        }

        public override int Depth
        {
            get { return _xmlreader.Depth; }
        }

        public override string BaseURI
        {
            get { return _xmlreader.BaseURI; }
        }

        public override bool IsEmptyElement
        {
            get { return _xmlreader.IsEmptyElement; }
        }

        public override bool IsDefault
        {
            get { return _xmlreader.IsDefault; }
        }

        public override char QuoteChar
        {
            get { return _xmlreader.QuoteChar; }
        }

        public override XmlSpace XmlSpace
        {
            get { return _xmlreader.XmlSpace; }
        }

        public override string XmlLang
        {
            get { return _xmlreader.XmlLang; }
        }

        public override int AttributeCount { get { return _xmlreader.AttributeCount; } }

        public override string? GetAttribute(string name)
        {
            return _xmlreader.GetAttribute(name);
        }

        public override string? GetAttribute(string localName, string? namespaceURI)
        {
            return _xmlreader.GetAttribute(localName, namespaceURI);
        }

        public override string GetAttribute(int i)
        {
            return _xmlreader.GetAttribute(i);
        }

        public override bool MoveToAttribute(string name)
        {
            return _xmlreader.MoveToAttribute(name);
        }

        public override bool MoveToAttribute(string localName, string? namespaceURI)
        {
            return _xmlreader.MoveToAttribute(localName, namespaceURI);
        }

        public override void MoveToAttribute(int i)
        {
            _xmlreader.MoveToAttribute(i);
        }

        public override bool MoveToFirstAttribute()
        {
            return _xmlreader.MoveToFirstAttribute();
        }

        public override bool MoveToNextAttribute()
        {
            return _xmlreader.MoveToNextAttribute();
        }

        public override bool MoveToElement()
        {
            return _xmlreader.MoveToElement();
        }

        public override bool ReadAttributeValue()
        {
            return _xmlreader.ReadAttributeValue();
        }

        public override bool Read()
        {
            return _xmlreader.Read();
        }

        public override bool EOF
        {
            get { return _xmlreader.EOF; }
        }

        public override void Close()
        {
            _xmlreader.Close();
        }

        public override ReadState ReadState
        {
            get { return _xmlreader.ReadState; }
        }

        public override void Skip()
        {
            _xmlreader.Skip();
        }

        public override XmlNameTable NameTable
        {
            get { return _xmlreader.NameTable; }
        }

        public override string? LookupNamespace(string prefix)
        {
            return _xmlreader.LookupNamespace(prefix);
        }

        public override bool CanResolveEntity
        {
            get { return _xmlreader.CanResolveEntity; }
        }

        public override void ResolveEntity()
        {
            _xmlreader.ResolveEntity();
        }

        public override bool CanReadBinaryContent
        {
            get { return _xmlreader.CanReadBinaryContent; }
        }

        public override int ReadContentAsBase64(byte[] buffer, int index, int count)
        {
            return _xmlreader.ReadContentAsBase64(buffer, index, count);
        }

        public override int ReadElementContentAsBase64(byte[] buffer, int index, int count)
        {
            return _xmlreader.ReadElementContentAsBase64(buffer, index, count);
        }

        public override int ReadContentAsBinHex(byte[] buffer, int index, int count)
        {
            return _xmlreader.ReadContentAsBinHex(buffer, index, count);
        }

        public override int ReadElementContentAsBinHex(byte[] buffer, int index, int count)
        {
            return _xmlreader.ReadElementContentAsBinHex(buffer, index, count);
        }

        public override bool CanReadValueChunk
        {
            get { return _xmlreader.CanReadValueChunk; }
        }

        public override string ReadString()
        {
            return _xmlreader.ReadString();
        }
    }
}
